Project import
diff --git a/0001-LinuxReleaseAth6kl-compat-wireless-add-11w-support.patch b/0001-LinuxReleaseAth6kl-compat-wireless-add-11w-support.patch
new file mode 100644
index 0000000..e6c7c0c
--- /dev/null
+++ b/0001-LinuxReleaseAth6kl-compat-wireless-add-11w-support.patch
@@ -0,0 +1,893 @@
+From 186f818bcf09ecd1290309d38844a349124c1386 Mon Sep 17 00:00:00 2001
+From: Shijie Zhang <shijiez@qca.qualcomm.com>
+Date: Mon, 20 May 2013 14:00:08 +0800
+Subject: [PATCH] LinuxWapiCcxReleaseAth6kl:compat-wireless: add 11w support
+ add optional support from wpa_supplicant to host driver for
+ linux packages Signed-off-by: Shijie Zhang
+ <shijiez@qca.qualcomm.com>
+
+---
+ config.mk | 1 +
+ drivers/net/wireless/ath/ath6kl/cfg80211.c | 103 +++++++++++++++++++++++--
+ drivers/net/wireless/ath/ath6kl/common.h | 3 +
+ drivers/net/wireless/ath/ath6kl/core.h | 10 +++
+ drivers/net/wireless/ath/ath6kl/debugfs_pri.c | 97 +++++++++++++++++++++++
+ drivers/net/wireless/ath/ath6kl/main.c | 14 ++++
+ drivers/net/wireless/ath/ath6kl/wmi.c | 90 +++++++++++++++++++++
+ drivers/net/wireless/ath/ath6kl/wmi.h | 44 +++++++++++
+ include/linux/ieee80211.h | 5 ++
+ include/linux/nl80211.h | 8 +-
+ include/net/cfg80211.h | 6 +-
+ net/mac80211/ieee80211_i.h | 6 +-
+ net/mac80211/mlme.c | 12 ++-
+ net/wireless/core.h | 4 +-
+ net/wireless/mlme.c | 8 +-
+ net/wireless/nl80211.c | 31 +++++---
+ net/wireless/sme.c | 4 +-
+ 17 files changed, 405 insertions(+), 41 deletions(-)
+
+diff --git a/config.mk b/config.mk
+index 5567d90..daad171 100644
+--- a/config.mk
++++ b/config.mk
+@@ -617,6 +617,7 @@ CONFIG_ATH6KL_SDIO=m
+ CONFIG_ATH6KL_USB=m
+ CONFIG_ATH6KL_DEBUG=y
+ CONFIG_ATH6KL_REGDOMAIN=y
++CONFIG_SUPPORT_11W=y
+ endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+ ifndef CONFIG_COMPAT_KERNEL_2_6_29
+diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
+index 7e8c827..8bcd06e 100644
+--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
++++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
+@@ -279,7 +279,24 @@ static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
+ vif->auth_mode = WPA_AUTH_CCKM;
+ else if (vif->auth_mode == WPA2_AUTH)
+ vif->auth_mode = WPA2_AUTH_CCKM;
++#ifdef CONFIG_SUPPORT_11W
++ } else if (key_mgmt == WLAN_AKM_SUITE_8021X_SHA256) {
++ if (vif->auth_mode == WPA_AUTH){
++ ath6kl_err("%s: auth_mode %x not supported key_mgmt %x\n", __func__, vif->auth_mode,key_mgmt);
++ return ;
++ }
++ else if (vif->auth_mode == WPA2_AUTH)
++ vif->auth_mode = WPA2_AUTH_SHA256;
++ } else if (key_mgmt == WLAN_AKM_SUITE_PSK_SHA256) {
++ if (vif->auth_mode == WPA_AUTH){
++ ath6kl_err("%s: auth_mode %x not supported key_mgmt %x\n", __func__, vif->auth_mode,key_mgmt);
++ return ;
++ }
++ else if (vif->auth_mode == WPA2_AUTH)
++ vif->auth_mode = WPA2_PSK_AUTH_SHA256;
++#endif
+ } else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
++
+ vif->auth_mode = NONE_AUTH;
+ }
+ }
+@@ -530,6 +547,21 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+ if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
+ memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
+
++#ifdef CONFIG_SUPPORT_11W
++ {
++ u16 rsn_cap = 0;
++
++ ath6kl_err("ath6kl_cfg80211_connect: sme->mfp = %d\n", sme->mfp);
++ if ( sme->mfp == NL80211_MFP_REQUIRED ) {
++ rsn_cap = 0xc0;
++ }else if ( sme->mfp == NL80211_MFP_OPTIONAL ) {
++ rsn_cap = 0x80;
++ }
++ ath6kl_wmi_set_rsn_cap_cmd(ar->wmi, vif->fw_vif_idx, rsn_cap);
++ }
++#endif
++
++
+ ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
+
+ status = ath6kl_set_auth_type(vif, sme->auth_type);
+@@ -1132,6 +1164,9 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
+ int seq_len;
+ u8 key_usage;
+ u8 key_type;
++#ifdef CONFIG_SUPPORT_11W
++ u8 max_key_index;
++#endif
+
+ if (!ath6kl_cfg80211_ready(vif))
+ return -EIO;
+@@ -1143,10 +1178,19 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
+ params->key);
+ }
+
++
++#ifdef CONFIG_SUPPORT_11W
++ if (WLAN_CIPHER_SUITE_AES_CMAC == params->cipher)
++ max_key_index = WMI_MAX_SUPPORT_11W_KEY_INDEX;
++ else
++ max_key_index = WMI_MAX_KEY_INDEX;
++ if (key_index > max_key_index) {
++#else
+ if (key_index > WMI_MAX_KEY_INDEX) {
++#endif
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+- "%s: key index %d out of bounds\n", __func__,
+- key_index);
++ "%s: key index %d out of bounds\n", __func__,
++ key_index);
+ return -ENOENT;
+ }
+
+@@ -1191,6 +1235,12 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
+ key_type = WAPI_CRYPT;
+ break;
+
++#ifdef CONFIG_SUPPORT_11W
++ case WLAN_CIPHER_SUITE_AES_CMAC:
++ key_type = AES_128_CMAC_CRYPT;
++ break;
++#endif
++
+ default:
+ return -ENOTSUPP;
+ }
+@@ -1240,11 +1290,24 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
+ return 0;
+ }
+
+- return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
+- key_type, key_usage, key->key_len,
+- key->seq, key->seq_len, key->key,
+- KEY_OP_INIT_VAL,
+- (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
++#ifdef CONFIG_SUPPORT_11W
++ if (AES_128_CMAC_CRYPT == key_type){
++ return ath6kl_wmi_addigtk_cmd(ar->wmi, vif->fw_vif_idx, key_index,
++ key_type, key_usage, key->key_len,
++ key->seq, key->seq_len, key->key,
++ KEY_OP_INIT_VAL,
++ (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
++ }else {
++#endif
++ return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
++ key_type, key_usage, key->key_len,
++ key->seq, key->seq_len, key->key,
++ KEY_OP_INIT_VAL,
++ (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
++#ifdef CONFIG_SUPPORT_11W
++ }
++#endif
++
+ }
+
+ static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
+@@ -1259,7 +1322,12 @@ static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
+ if (!ath6kl_cfg80211_ready(vif))
+ return -EIO;
+
++#ifdef CONFIG_SUPPORT_11W
++ if (key_index > WMI_MAX_SUPPORT_11W_KEY_INDEX) {
++#else
+ if (key_index > WMI_MAX_KEY_INDEX) {
++#endif
++
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "%s: key index %d out of bounds\n", __func__,
+ key_index);
+@@ -1711,6 +1779,9 @@ static const u32 cipher_suites[] = {
+ WLAN_CIPHER_SUITE_CCMP,
+ CCKM_KRK_CIPHER_SUITE,
+ WLAN_CIPHER_SUITE_SMS4,
++#ifdef CONFIG_SUPPORT_11W
++ WLAN_CIPHER_SUITE_AES_CMAC,
++#endif
+ };
+
+ static bool is_rate_legacy(s32 rate)
+@@ -2789,6 +2860,20 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
+ if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+ p.auth_mode |= WPA2_PSK_AUTH;
+ break;
++#ifdef CONFIG_SUPPORT_11W
++ case WLAN_AKM_SUITE_8021X_SHA256:
++ if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
++ ath6kl_err("WLAN_AKM_SUITE_8021X_SHA256 is not supported in wpa_versions %x\n",info->crypto.wpa_versions);
++ if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
++ p.auth_mode |= WPA2_AUTH_SHA256;
++ break;
++ case WLAN_AKM_SUITE_PSK_SHA256:
++ if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
++ ath6kl_err("WLAN_AKM_SUITE_PSK_SHA256 is not supported in wpa_versions %x\n",info->crypto.wpa_versions);
++ if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
++ p.auth_mode |= WPA2_PSK_AUTH_SHA256;
++ break;
++#endif
+ }
+ }
+ if (p.auth_mode == 0)
+@@ -2858,7 +2943,11 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
+ * advertise the same in beacon/probe response. Send
+ * the complete RSN IE capability field to firmware
+ */
++#ifdef CONFIG_SUPPORT_11W
++ if ((p.auth_mode & (WPA2_AUTH | WPA2_PSK_AUTH | WPA2_AUTH_SHA256 | WPA2_PSK_AUTH_SHA256)) && (info->tail) &&
++#else
+ if ((p.auth_mode & (WPA2_AUTH | WPA2_PSK_AUTH)) && (info->tail) &&
++#endif
+ test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
+ ar->fw_capabilities)) {
+ rsn_capb = 0;
+diff --git a/drivers/net/wireless/ath/ath6kl/common.h b/drivers/net/wireless/ath/ath6kl/common.h
+index f78c59c..622063d 100644
+--- a/drivers/net/wireless/ath/ath6kl/common.h
++++ b/drivers/net/wireless/ath/ath6kl/common.h
+@@ -73,6 +73,9 @@ enum crypto_type {
+ TKIP_CRYPT = 0x04,
+ AES_CRYPT = 0x08,
+ WAPI_CRYPT = 0x10,
++#ifdef CONFIG_SUPPORT_11W
++ AES_128_CMAC_CRYPT = 0x20,
++#endif
+ };
+
+ struct htc_endpoint_credit_dist;
+diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h
+index 1a97a1f..ac240df 100644
+--- a/drivers/net/wireless/ath/ath6kl/core.h
++++ b/drivers/net/wireless/ath/ath6kl/core.h
+@@ -599,7 +599,11 @@ struct ath6kl_vif {
+ u16 ch_hint;
+ u16 bss_ch;
+ struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1];
++#ifdef CONFIG_SUPPORT_11W
++ struct ath6kl_key keys[WMI_MAX_SUPPORT_11W_KEY_INDEX + 1];
++#else
+ struct ath6kl_key keys[WMI_MAX_KEY_INDEX + 1];
++#endif
+ struct aggr_info *aggr_cntxt;
+ struct ath6kl_htcap htcap[IEEE80211_NUM_BANDS];
+
+@@ -628,6 +632,9 @@ struct ath6kl_vif {
+ struct wmi_connect_cmd profile;
+ u16 rsn_capab;
+
++#ifdef CONFIG_SUPPORT_11W
++ u16 rsn_cap; /* for 802.11w */
++#endif
+ struct list_head mc_filter;
+ };
+
+@@ -914,6 +921,9 @@ void ath6kl_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, bool ismcast);
+ void ath6kl_txpwr_rx_evt(void *devt, u8 tx_pwr);
+ void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status);
+ void ath6kl_tgt_stats_event(struct ath6kl_vif *vif, u8 *ptr, u32 len);
++#ifdef CONFIG_SUPPORT_11W
++void ath6kl_get_rsn_cap_event(struct ath6kl_vif *vif, u16 rsn_cap);
++#endif
+ void ath6kl_indicate_tx_activity(void *devt, u8 traffic_class, bool active);
+ enum htc_endpoint_id ath6kl_ac2_endpoint_id(void *devt, u8 ac);
+
+diff --git a/drivers/net/wireless/ath/ath6kl/debugfs_pri.c b/drivers/net/wireless/ath/ath6kl/debugfs_pri.c
+index 79a4746..34682be 100644
+--- a/drivers/net/wireless/ath/ath6kl/debugfs_pri.c
++++ b/drivers/net/wireless/ath/ath6kl/debugfs_pri.c
+@@ -195,6 +195,99 @@ static const struct file_operations fops_bmisstime = {
+ .llseek = default_llseek,
+ };
+
++#ifdef CONFIG_SUPPORT_11W
++static ssize_t ath6kl_rsn_cap_write(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath6kl *ar = file->private_data;
++ struct ath6kl_vif *vif;
++ u16 rsn_cap;
++ char buf[32];
++ ssize_t len;
++ int ret;
++
++ if (WARN_ON(!test_bit(WMI_READY, &ar->flag)))
++ return -EIO;
++
++ vif = ath6kl_vif_first(ar);
++ if (!vif)
++ return -EIO;
++
++ len = min(count, sizeof(buf) - 1);
++ if (copy_from_user(buf, user_buf, len))
++ return -EFAULT;
++
++ buf[len] = '\0';
++ if (kstrtou16(buf, 0, &rsn_cap))
++ return -EINVAL;
++
++ vif->rsn_cap = rsn_cap;
++
++ ret = ath6kl_wmi_set_rsn_cap_cmd(ar->wmi, vif->fw_vif_idx,
++ vif->rsn_cap);
++ if (ret) {
++ ath6kl_err("failed to set rsn cap:%d\n", ret);
++ return ret;
++ }
++
++ return count;
++}
++
++static ssize_t ath6kl_rsn_cap_read(struct file *file, char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath6kl *ar = file->private_data;
++ struct ath6kl_vif *vif;
++ char buf[32];
++ long left;
++ int ret;
++ ssize_t len;
++
++ if (WARN_ON(!test_bit(WMI_READY, &ar->flag)))
++ return -EIO;
++
++ vif = ath6kl_vif_first(ar);
++ if (!vif)
++ return -EIO;
++
++ if (down_interruptible(&ar->sem)) {
++ return -EBUSY;
++ }
++
++ set_bit(STATS_UPDATE_PEND, &vif->flags);
++
++ ret = ath6kl_wmi_get_rsn_cap_cmd(ar->wmi, vif->fw_vif_idx);
++ if (ret) {
++ up(&ar->sem);
++ ath6kl_err("ath6kl_wmi_get_rsn_cap_cmd failed:%d \n", ret);
++ return -EIO;
++ }
++
++ left = wait_event_interruptible_timeout(ar->event_wq,
++ !test_bit(STATS_UPDATE_PEND,
++ &vif->flags), WMI_TIMEOUT);
++
++ up(&ar->sem);
++
++ if (left <= 0) {
++ ath6kl_err("wait_event_interruptible_timeout\n");
++ return -ETIMEDOUT;
++ }
++
++ len = scnprintf(buf, sizeof(buf), "%u\n", vif->rsn_cap);
++ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++}
++
++static const struct file_operations fops_rsn_cap = {
++ .open = ath6kl_debugfs_open_pri,
++ .read = ath6kl_rsn_cap_read,
++ .write = ath6kl_rsn_cap_write,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++#endif
++
+ int ath6kl_init_debugfs_pri(struct ath6kl *ar)
+ {
+ debugfs_create_file("inactivity_period", S_IWUSR, ar->debugfs_phy, ar,
+@@ -202,6 +295,10 @@ int ath6kl_init_debugfs_pri(struct ath6kl *ar)
+
+ debugfs_create_file("bmiss_time", S_IRUSR | S_IWUSR, ar->debugfs_phy,
+ ar, &fops_bmisstime);
++#ifdef CONFIG_SUPPORT_11W
++ debugfs_create_file("rsn_cap", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar,
++ &fops_rsn_cap);
++#endif
+
+ return 0;
+ }
+diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c
+index 3280f05..21cc7a9 100644
+--- a/drivers/net/wireless/ath/ath6kl/main.c
++++ b/drivers/net/wireless/ath/ath6kl/main.c
+@@ -910,6 +910,20 @@ void ath6kl_tgt_stats_event(struct ath6kl_vif *vif, u8 *ptr, u32 len)
+ }
+ }
+
++#ifdef CONFIG_SUPPORT_11W
++void ath6kl_get_rsn_cap_event(struct ath6kl_vif *vif, u16 rsn_cap)
++{
++ struct ath6kl *ar = vif->ar;
++
++ vif->rsn_cap = rsn_cap;
++
++ if (test_bit(STATS_UPDATE_PEND, &vif->flags)) {
++ clear_bit(STATS_UPDATE_PEND, &vif->flags);
++ wake_up(&ar->event_wq);
++ }
++}
++#endif
++
+ void ath6kl_wakeup_event(void *dev)
+ {
+ struct ath6kl *ar = (struct ath6kl *) dev;
+diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
+index 8aebf56..49cb0af 100644
+--- a/drivers/net/wireless/ath/ath6kl/wmi.c
++++ b/drivers/net/wireless/ath/ath6kl/wmi.c
+@@ -1338,6 +1338,22 @@ static int ath6kl_wmi_stats_event_rx(struct wmi *wmi, u8 *datap, int len,
+ return 0;
+ }
+
++#ifdef CONFIG_SUPPORT_11W
++static int ath6kl_wmi_get_rsn_cap_rx(struct wmi *wmi, u8 *datap, int len,
++ struct ath6kl_vif *vif)
++{
++ struct wmi_rsn_cap_cmd *reply;
++
++ if (len < sizeof(struct wmi_rsn_cap_cmd))
++ return -EINVAL;
++
++ reply = (struct wmi_rsn_cap_cmd *) datap;
++ ath6kl_get_rsn_cap_event(vif, le16_to_cpu(reply->rsn_cap));
++
++ return 0;
++}
++#endif
++
+ static u8 ath6kl_wmi_get_upper_threshold(s16 rssi,
+ struct sq_threshold_params *sq_thresh,
+ u32 size)
+@@ -2198,6 +2214,32 @@ int ath6kl_wmi_bmisstime_cmd(struct wmi *wmi, u8 if_idx,
+ return ret;
+ }
+
++#ifdef CONFIG_SUPPORT_11W
++int ath6kl_wmi_set_rsn_cap_cmd(struct wmi *wmi, u8 if_idx,
++ u16 rsn_cap)
++{
++ struct sk_buff *skb;
++ struct wmi_rsn_cap_cmd *cmd;
++ int ret;
++
++ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
++ if (!skb)
++ return -ENOMEM;
++
++ cmd = (struct wmi_rsn_cap_cmd *) skb->data;
++ cmd->rsn_cap = cpu_to_le16(rsn_cap);
++
++ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_RSN_CAP_CMDID,
++ NO_SYNC_WMIFLAG);
++ return ret;
++}
++
++int ath6kl_wmi_get_rsn_cap_cmd(struct wmi *wmi, u8 if_idx)
++{
++ return ath6kl_wmi_simple_cmd(wmi, if_idx, WMI_GET_RSN_CAP_CMDID);
++}
++#endif
++
+ int ath6kl_wmi_powermode_cmd(struct wmi *wmi, u8 if_idx, u8 pwr_mode)
+ {
+ struct sk_buff *skb;
+@@ -2313,6 +2355,48 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index,
+ return ret;
+ }
+
++#ifdef CONFIG_SUPPORT_11W
++int ath6kl_wmi_addigtk_cmd(struct wmi *wmi, u8 if_idx, u8 key_index,
++ enum crypto_type key_type,
++ u8 key_usage, u8 key_len,
++ u8 *key_rsc, unsigned int key_rsc_len,
++ u8 *key_material,
++ u8 key_op_ctrl, u8 *mac_addr,
++ enum wmi_sync_flag sync_flag)
++{
++ struct sk_buff *skb;
++ struct wmi_add_igtk_key_cmd *cmd;
++ int ret;
++
++ ath6kl_dbg(ATH6KL_DBG_WMI, "addigtk cmd: key_index=%u key_type=%d "
++ "key_usage=%d key_len=%d key_op_ctrl=%d\n",
++ key_index, key_type, key_usage, key_len, key_op_ctrl);
++
++ if ((key_index > WMI_MAX_SUPPORT_11W_KEY_INDEX) || (key_len > WMI_MAX_KEY_LEN) ||
++ (key_material == NULL) || key_rsc_len > 6)
++ return -EINVAL;
++
++ if ((WEP_CRYPT != key_type) && (NULL == key_rsc))
++ return -EINVAL;
++
++ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
++ if (!skb)
++ return -ENOMEM;
++
++ cmd = (struct wmi_add_igtk_key_cmd *) skb->data;
++ cmd->key_index = key_index;
++ cmd->key_len = key_len;
++ memcpy(cmd->key, key_material, key_len);
++ if (key_rsc != NULL)
++ memcpy(cmd->key_rsc, key_rsc, key_rsc_len);
++
++ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_IGTK_CMDID,
++ sync_flag);
++
++ return ret;
++}
++#endif
++
+ int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk)
+ {
+ struct sk_buff *skb;
+@@ -3940,6 +4024,12 @@ static int ath6kl_wmi_proc_events_vif(struct net_device *dev, struct wmi *wmi,
+ ret = ath6kl_wmi_neighbor_report_event_rx(wmi, datap, len,
+ vif);
+ break;
++#ifdef CONFIG_SUPPORT_11W
++ case WMI_GET_RSN_CAP_EVENTID:
++ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n");
++ ret = ath6kl_wmi_get_rsn_cap_rx(wmi, datap, len, vif);
++ break;
++#endif
+ case WMI_SCAN_COMPLETE_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n");
+ ret = ath6kl_wmi_scan_complete_rx(wmi, datap, len, vif);
+diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h
+index da3612c..661e8ad 100644
+--- a/drivers/net/wireless/ath/ath6kl/wmi.h
++++ b/drivers/net/wireless/ath/ath6kl/wmi.h
+@@ -685,10 +685,18 @@ enum auth_mode {
+ WPA2_PSK_AUTH = 0x10,
+ WPA_AUTH_CCKM = 0x20,
+ WPA2_AUTH_CCKM = 0x40,
++#ifdef CONFIG_SUPPORT_11W
++ WPA2_AUTH_SHA256 = 0x80,
++ WPA2_PSK_AUTH_SHA256 = 0x81,
++#endif
+ };
+
+ #define WMI_MAX_KEY_INDEX 3
+
++#ifdef CONFIG_SUPPORT_11W
++#define WMI_MAX_SUPPORT_11W_KEY_INDEX 5
++#endif
++
+ #define WMI_MAX_KEY_LEN 32
+
+ /*
+@@ -797,6 +805,18 @@ struct wmi_delete_cipher_key_cmd {
+ u8 key_index;
+ } __packed;
+
++#ifdef CONFIG_SUPPORT_11W
++struct wmi_add_igtk_key_cmd {
++ u8 key_index;
++
++ u8 key_len;
++
++ u8 key_rsc[6];/* key replay sequence counter */
++
++ u8 key[WMI_MAX_KEY_LEN];
++} __packed;
++#endif
++
+ #define WMI_KRK_LEN 16
+
+ /* WMI_ADD_KRK_CMDID */
+@@ -2159,6 +2179,16 @@ struct wmi_txe_notify_event {
+ __le32 pkts;
+ } __packed;
+
++#ifdef CONFIG_SUPPORT_11W
++#define RSN_CAP_PREAUTH 0x01
++#define RSN_CAP_MFPR 0x40
++#define RSN_CAP_MFPC 0x80
++
++struct wmi_rsn_cap_cmd {
++ __le16 rsn_cap;
++} __packed;
++#endif
++
+ /* WMI_SET_AKMP_PARAMS_CMD */
+
+ struct wmi_pmkid {
+@@ -2656,6 +2686,16 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index,
+ u8 *key_material,
+ u8 key_op_ctrl, u8 *mac_addr,
+ enum wmi_sync_flag sync_flag);
++#ifdef CONFIG_SUPPORT_11W
++int ath6kl_wmi_addigtk_cmd(struct wmi *wmi, u8 if_idx, u8 key_index,
++ enum crypto_type key_type,
++ u8 key_usage, u8 key_len,
++ u8 *key_rsc, unsigned int key_rsc_len,
++ u8 *key_material,
++ u8 key_op_ctrl, u8 *mac_addr,
++ enum wmi_sync_flag sync_flag);
++#endif
++
+ int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk);
+ int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index);
+ int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, u8 if_idx, const u8 *bssid,
+@@ -2665,6 +2705,10 @@ int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi, u8 if_idx);
+ int ath6kl_wmi_get_roam_tbl_cmd(struct wmi *wmi);
+
+ int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, u8 if_idx, enum wmi_txop_cfg cfg);
++#ifdef CONFIG_SUPPORT_11W
++int ath6kl_wmi_get_rsn_cap_cmd(struct wmi *wmi, u8 if_idx);
++int ath6kl_wmi_set_rsn_cap_cmd(struct wmi *wmi, u8 if_idx, u16 rsn_cap);
++#endif
+ int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 if_idx,
+ u8 keep_alive_intvl);
+ int ath6kl_wmi_set_htcap_cmd(struct wmi *wmi, u8 if_idx,
+diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
+index 66cedf6..8f680c8 100644
+--- a/include/linux/ieee80211.h
++++ b/include/linux/ieee80211.h
+@@ -1566,6 +1566,11 @@ enum ieee80211_sa_query_action {
+
+ #define WLAN_MAX_KEY_LEN 32
+
++#ifdef CONFIG_SUPPORT_11W
++#define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05
++#define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06
++#endif
++
+ #define WLAN_PMKID_LEN 16
+
+ #define WLAN_OUI_WFA 0x506f9a
+diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
+index 85e5051..d9854f4 100644
+--- a/include/linux/nl80211.h
++++ b/include/linux/nl80211.h
+@@ -363,8 +363,8 @@
+ * requests to connect to a specified network but without separating
+ * auth and assoc steps. For this, you need to specify the SSID in a
+ * %NL80211_ATTR_SSID attribute, and can optionally specify the association
+- * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC,
+- * %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
++ * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
++ * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
+ * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
+ * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
+ * Background scan period can optionally be
+@@ -914,7 +914,7 @@ enum nl80211_commands {
+ * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is
+ * used for the association (&enum nl80211_mfp, represented as a u32);
+ * this attribute can be used
+- * with %NL80211_CMD_ASSOCIATE request
++ * with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests
+ *
+ * @NL80211_ATTR_STA_FLAGS2: Attribute containing a
+ * &struct nl80211_sta_flag_update.
+@@ -2380,10 +2380,12 @@ enum nl80211_key_type {
+ * enum nl80211_mfp - Management frame protection state
+ * @NL80211_MFP_NO: Management frame protection not used
+ * @NL80211_MFP_REQUIRED: Management frame protection required
++ * @NL80211_MFP_OPTIONAL: Management frame protection enabled, but not required
+ */
+ enum nl80211_mfp {
+ NL80211_MFP_NO,
+ NL80211_MFP_REQUIRED,
++ NL80211_MFP_OPTIONAL,
+ };
+
+ enum nl80211_wpa_versions {
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index a98830b..f9e658b 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -1056,7 +1056,7 @@ struct cfg80211_auth_request {
+ * @bss: The BSS to associate with.
+ * @ie: Extra IEs to add to (Re)Association Request frame or %NULL
+ * @ie_len: Length of ie buffer in octets
+- * @use_mfp: Use management frame protection (IEEE 802.11w) in this association
++ * @mfp: indicate whether management frame protection is used
+ * @crypto: crypto settings
+ * @prev_bssid: previous BSSID, if not %NULL use reassociate frame
+ */
+@@ -1065,7 +1065,7 @@ struct cfg80211_assoc_request {
+ const u8 *ie, *prev_bssid;
+ size_t ie_len;
+ struct cfg80211_crypto_settings crypto;
+- bool use_mfp;
++ enum nl80211_mfp mfp;
+ };
+
+ /**
+@@ -1160,6 +1160,7 @@ struct cfg80211_ibss_params {
+ * @ie: IEs for association request
+ * @ie_len: Length of assoc_ie in octets
+ * @privacy: indicates whether privacy-enabled APs should be used
++ * @mfp: indicate whether management frame protection is used
+ * @crypto: crypto settings
+ * @key_len: length of WEP key for shared key authentication
+ * @key_idx: index of WEP key for shared key authentication
+@@ -1176,6 +1177,7 @@ struct cfg80211_connect_params {
+ u8 *ie;
+ size_t ie_len;
+ bool privacy;
++ enum nl80211_mfp mfp;
+ struct cfg80211_crypto_settings crypto;
+ const u8 *key;
+ u8 key_len, key_idx;
+diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
+index a01e37f..9292440 100644
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -406,11 +406,7 @@ struct ieee80211_if_managed {
+ bool beacon_crc_valid;
+ u32 beacon_crc;
+
+- enum {
+- IEEE80211_MFP_DISABLED,
+- IEEE80211_MFP_OPTIONAL,
+- IEEE80211_MFP_REQUIRED
+- } mfp; /* management frame protection */
++ enum nl80211_mfp mfp; /* management frame protection */
+
+ int wmm_last_param_set;
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 9d1fb85..8838816 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -2704,13 +2704,11 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
+ else
+ wk->type = IEEE80211_WORK_ASSOC;
+
+- if (req->use_mfp) {
+- ifmgd->mfp = IEEE80211_MFP_REQUIRED;
+- ifmgd->flags |= IEEE80211_STA_MFP_ENABLED;
+- } else {
+- ifmgd->mfp = IEEE80211_MFP_DISABLED;
+- ifmgd->flags &= ~IEEE80211_STA_MFP_ENABLED;
+- }
++ ifmgd->mfp = req->mfp;
++ if (req->mfp != NL80211_MFP_NO)
++ ifmgd->flags |= IEEE80211_STA_MFP_ENABLED;
++ else
++ ifmgd->flags &= ~IEEE80211_STA_MFP_ENABLED;
+
+ if (req->crypto.control_port)
+ ifmgd->flags |= IEEE80211_STA_CONTROL_PORT;
+diff --git a/net/wireless/core.h b/net/wireless/core.h
+index 11ff6bb..61c92d3 100644
+--- a/net/wireless/core.h
++++ b/net/wireless/core.h
+@@ -339,13 +339,13 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+ struct ieee80211_channel *chan,
+ const u8 *bssid, const u8 *prev_bssid,
+ const u8 *ssid, int ssid_len,
+- const u8 *ie, int ie_len, bool use_mfp,
++ const u8 *ie, int ie_len, enum nl80211_mfp mfp,
+ struct cfg80211_crypto_settings *crypt);
+ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, struct ieee80211_channel *chan,
+ const u8 *bssid, const u8 *prev_bssid,
+ const u8 *ssid, int ssid_len,
+- const u8 *ie, int ie_len, bool use_mfp,
++ const u8 *ie, int ie_len, enum nl80211_mfp mfp,
+ struct cfg80211_crypto_settings *crypt);
+ int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *bssid,
+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
+index 4d74154..25abf54 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -506,7 +506,7 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+ struct ieee80211_channel *chan,
+ const u8 *bssid, const u8 *prev_bssid,
+ const u8 *ssid, int ssid_len,
+- const u8 *ie, int ie_len, bool use_mfp,
++ const u8 *ie, int ie_len, enum nl80211_mfp mfp,
+ struct cfg80211_crypto_settings *crypt)
+ {
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+@@ -535,7 +535,7 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+ req.ie = ie;
+ req.ie_len = ie_len;
+ memcpy(&req.crypto, crypt, sizeof(req.crypto));
+- req.use_mfp = use_mfp;
++ req.mfp = mfp;
+ req.prev_bssid = prev_bssid;
+ req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
+ WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
+@@ -573,7 +573,7 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+ struct ieee80211_channel *chan,
+ const u8 *bssid, const u8 *prev_bssid,
+ const u8 *ssid, int ssid_len,
+- const u8 *ie, int ie_len, bool use_mfp,
++ const u8 *ie, int ie_len, enum nl80211_mfp mfp,
+ struct cfg80211_crypto_settings *crypt)
+ {
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+@@ -581,7 +581,7 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+
+ wdev_lock(wdev);
+ err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
+- ssid, ssid_len, ie, ie_len, use_mfp, crypt);
++ ssid, ssid_len, ie, ie_len, mfp, crypt);
+ wdev_unlock(wdev);
+
+ return err;
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 896d19b..0e69a75 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -4493,7 +4493,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
+ struct ieee80211_channel *chan;
+ const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
+ int err, ssid_len, ie_len = 0;
+- bool use_mfp = false;
++ enum nl80211_mfp mfp;
+
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+@@ -4525,14 +4525,15 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
+ ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ }
+
+- if (info->attrs[NL80211_ATTR_USE_MFP]) {
+- enum nl80211_mfp mfp =
+- nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
+- if (mfp == NL80211_MFP_REQUIRED)
+- use_mfp = true;
+- else if (mfp != NL80211_MFP_NO)
+- return -EINVAL;
+- }
++ if (info->attrs[NL80211_ATTR_USE_MFP]) {
++ mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
++ if (mfp != NL80211_MFP_REQUIRED &&
++ mfp != NL80211_MFP_OPTIONAL &&
++ mfp != NL80211_MFP_NO)
++ return -EINVAL;
++ }else{
++ mfp = NL80211_MFP_NO;
++ }
+
+ if (info->attrs[NL80211_ATTR_PREV_BSSID])
+ prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
+@@ -4540,7 +4541,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
+ err = nl80211_crypto_settings(rdev, info, &crypto, 1);
+ if (!err)
+ err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
+- ssid, ssid_len, ie, ie_len, use_mfp,
++ ssid, ssid_len, ie, ie_len, mfp,
+ &crypto);
+
+ return err;
+@@ -5022,6 +5023,16 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
+ connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ }
+
++ if (info->attrs[NL80211_ATTR_USE_MFP]) {
++ connect.mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
++ if (connect.mfp != NL80211_MFP_REQUIRED &&
++ connect.mfp != NL80211_MFP_OPTIONAL &&
++ connect.mfp != NL80211_MFP_NO)
++ return -EINVAL;
++ } else {
++ connect.mfp = NL80211_MFP_NO;
++ }
++
+ if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ connect.channel =
+ ieee80211_get_channel(wiphy,
+diff --git a/net/wireless/sme.c b/net/wireless/sme.c
+index 38d3248..f02e0bc 100644
+--- a/net/wireless/sme.c
++++ b/net/wireless/sme.c
+@@ -190,7 +190,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
+ prev_bssid,
+ params->ssid, params->ssid_len,
+ params->ie, params->ie_len,
+- false, ¶ms->crypto);
++ params->mfp,
++ ¶ms->crypto);
++
+ if (err)
+ __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
+ NULL, 0,
+--
+1.7.9.5
+
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644
index 0000000..ca442d3
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,356 @@
+
+ NOTE! This copyright does *not* cover user programs that use kernel
+ services by normal system calls - this is merely considered normal use
+ of the kernel, and does *not* fall under the heading of "derived work".
+ Also note that the GPL below is copyrighted by the Free Software
+ Foundation, but the instance of code that it refers to (the Linux
+ kernel) is copyrighted by me and others who actually wrote it.
+
+ Also note that the only valid version of the GPL as far as the kernel
+ is concerned is _this_ particular version of the license (ie v2, not
+ v2.2 or v3.x or whatever), unless explicitly otherwise stated.
+
+ Linus Torvalds
+
+----------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..499e419
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,471 @@
+export KMODDIR?= updates
+KMODDIR_ARG:= "INSTALL_MOD_DIR=$(KMODDIR)"
+ifneq ($(origin KLIB), undefined)
+KMODPATH_ARG:= "INSTALL_MOD_PATH=$(KLIB)"
+else
+export KLIB:= /lib/modules/$(shell uname -r)
+endif
+export KLIB_BUILD ?= $(KLIB)/build
+# Sometimes not available in the path
+MODPROBE := /sbin/modprobe
+MADWIFI=$(shell $(MODPROBE) -l ath_pci)
+OLD_IWL=$(shell $(MODPROBE) -l iwl4965)
+
+DESTDIR?=
+
+ifneq ($(KERNELRELEASE),)
+
+NOSTDINC_FLAGS := -I$(M)/include/ \
+ -include $(M)/include/linux/compat-2.6.h \
+ $(CFLAGS)
+
+obj-y := compat/
+
+obj-$(CONFIG_COMPAT_RFKILL) += net/rfkill/
+
+ifeq ($(BT),)
+obj-$(CONFIG_COMPAT_WIRELESS) += net/wireless/ net/mac80211/
+obj-$(CONFIG_COMPAT_WIRELESS_MODULES) += drivers/net/wireless/
+
+obj-$(CONFIG_COMPAT_NET_USB_MODULES) += drivers/net/usb/
+
+obj-$(CONFIG_COMPAT_NETWORK_MODULES) += drivers/net/ethernet/atheros/
+obj-$(CONFIG_COMPAT_NETWORK_MODULES) += drivers/net/ethernet/broadcom/
+
+obj-$(CONFIG_COMPAT_VAR_MODULES) += drivers/ssb/
+obj-$(CONFIG_COMPAT_VAR_MODULES) += drivers/bcma/
+obj-$(CONFIG_COMPAT_VAR_MODULES) += drivers/misc/eeprom/
+
+ifeq ($(CONFIG_STAGING_EXCLUDE_BUILD),)
+endif
+
+endif
+
+obj-$(CONFIG_COMPAT_BLUETOOTH) += net/bluetooth/
+obj-$(CONFIG_COMPAT_BLUETOOTH_MODULES) += drivers/bluetooth/
+
+else
+
+export PWD := $(shell pwd)
+CFLAGS += \
+ -DCOMPAT_BASE_TREE="\"$(shell cat compat_base_tree)\"" \
+ -DCOMPAT_BASE_TREE_VERSION="\"$(shell cat compat_base_tree_version)\"" \
+ -DCOMPAT_PROJECT="\"Compat-wireless\"" \
+ -DCOMPAT_VERSION="\"$(shell cat compat_version)\""
+
+# These exported as they are used by the scripts
+# to check config and compat autoconf
+export COMPAT_CONFIG=config.mk
+export CONFIG_CHECK=.$(COMPAT_CONFIG)_md5sum.txt
+export COMPAT_AUTOCONF=include/linux/compat_autoconf.h
+export CREL=$(shell cat $(PWD)/compat_version)
+export CREL_PRE:=.compat_autoconf_
+export CREL_CHECK:=$(CREL_PRE)$(CREL)
+
+include $(PWD)/$(COMPAT_CONFIG)
+
+all: modules
+
+modules: $(CREL_CHECK)
+ @./scripts/check_config.sh
+ $(MAKE) -C $(KLIB_BUILD) M=$(PWD) modules
+ @touch $@
+
+bt: $(CREL_CHECK)
+ @./scripts/check_config.sh
+ $(MAKE) -C $(KLIB_BUILD) M=$(PWD) BT=TRUE modules
+ @touch $@
+
+# With the above and this we make sure we generate a new compat autoconf per
+# new relase of compat-wireless-2.6 OR when the user updates the
+# $(COMPAT_CONFIG) file
+$(CREL_CHECK):
+ @# Force to regenerate compat autoconf
+ @rm -f $(CONFIG_CHECK)
+ @./scripts/check_config.sh
+ @touch $@
+ @md5sum $(COMPAT_CONFIG) > $(CONFIG_CHECK)
+
+btinstall: btuninstall bt-install-modules
+
+bt-install-modules: bt
+ $(MAKE) -C $(KLIB_BUILD) M=$(PWD) $(KMODDIR_ARG) $(KMODPATH_ARG) BT=TRUE \
+ modules_install
+ @/sbin/depmod -ae
+ @echo
+ @echo "Currently detected bluetooth subsystem modules:"
+ @echo
+ @$(MODPROBE) -l ath3k
+ @$(MODPROBE) -l bcm203x
+ @$(MODPROBE) -l bluecard_cs
+ @$(MODPROBE) -l bluetooth
+ @$(MODPROBE) -l bnep
+ @$(MODPROBE) -l bpa10x
+ @$(MODPROBE) -l bt3c_cs
+ @$(MODPROBE) -l btmrvl
+ @$(MODPROBE) -l btmrvl_sdio
+ @$(MODPROBE) -l btsdio
+ @$(MODPROBE) -l btusb
+ @$(MODPROBE) -l btuart_cs
+ @$(MODPROBE) -l cmtp
+ @$(MODPROBE) -l dtl1_cs
+ @$(MODPROBE) -l hidp
+ @$(MODPROBE) -l hci_vhci
+ @$(MODPROBE) -l hci_uart
+ @$(MODPROBE) -l l2cap
+ @$(MODPROBE) -l rfcomm
+ @$(MODPROBE) -l sco
+ @echo
+ @echo Now run:
+ @echo
+ @echo sudo make btunload:
+ @echo
+ @echo And then load the needed bluetooth modules. If unsure reboot.
+ @echo
+
+btuninstall:
+ @# New location, matches upstream
+ @rm -rf $(KLIB)/$(KMODDIR)/net/bluetooth/
+ @rm -rf $(KLIB)/$(KMODDIR)/drivers/bluetooth/
+ @# Lets only remove the stuff we are sure we are providing
+ @# on the misc directory.
+ @/sbin/depmod -ae
+ @echo
+ @echo "Your old bluetooth subsystem modules were left intact:"
+ @echo
+ @$(MODPROBE) -l ath3k
+ @$(MODPROBE) -l bcm203x
+ @$(MODPROBE) -l bluecard_cs
+ @$(MODPROBE) -l bluetooth
+ @$(MODPROBE) -l bnep
+ @$(MODPROBE) -l bpa10x
+ @$(MODPROBE) -l bt3c_cs
+ @$(MODPROBE) -l btmrvl
+ @$(MODPROBE) -l btmrvl_sdio
+ @$(MODPROBE) -l btsdio
+ @$(MODPROBE) -l btusb
+ @$(MODPROBE) -l btuart_cs
+ @$(MODPROBE) -l cmtp
+ @$(MODPROBE) -l dtl1_cs
+ @$(MODPROBE) -l hidp
+ @$(MODPROBE) -l hci_vhci
+ @$(MODPROBE) -l hci_uart
+ @$(MODPROBE) -l l2cap
+ @$(MODPROBE) -l rfcomm
+ @$(MODPROBE) -l sco
+ @echo
+
+btclean:
+ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) BT=TRUE clean
+ @rm -f $(CREL_PRE)*
+
+install: uninstall install-modules install-scripts
+
+install-modules: modules
+ $(MAKE) -C $(KLIB_BUILD) M=$(PWD) $(KMODDIR_ARG) $(KMODPATH_ARG) \
+ modules_install
+ @./scripts/update-initramfs
+
+install-scripts:
+ @# All the scripts we can use
+ @mkdir -p $(DESTDIR)/usr/lib/compat-wireless/
+ @install scripts/modlib.sh $(DESTDIR)/usr/lib/compat-wireless/
+ @install scripts/madwifi-unload $(DESTDIR)/usr/sbin/
+ @# This is to allow switching between drivers without blacklisting
+ @install scripts/athenable $(DESTDIR)/usr/sbin/
+ @install scripts/b43enable $(DESTDIR)/usr/sbin/
+ @install scripts/iwl-enable $(DESTDIR)/usr/sbin/
+ @install scripts/athload $(DESTDIR)/usr/sbin/
+ @install scripts/b43load $(DESTDIR)/usr/sbin/
+ @install scripts/iwl-load $(DESTDIR)/usr/sbin/
+ @if [ ! -z "$(MADWIFI)" ] && [ -z "$(DESTDIR)" ]; then \
+ echo ;\
+ echo -n "Note: madwifi detected, we're going to disable it. " ;\
+ echo "If you would like to enable it later you can run:" ;\
+ echo " sudo athenable madwifi" ;\
+ echo ;\
+ echo Running athenable ath5k...;\
+ /usr/sbin/athenable ath5k ;\
+ fi
+ @if [ ! -z "$(OLD_IWL)" ] && [ -z "$(DESTDIR)" ]; then \
+ echo ;\
+ echo -n "Note: iwl4965 detected, we're going to disable it. " ;\
+ echo "If you would like to enable it later you can run:" ;\
+ echo " sudo iwl-load iwl4965" ;\
+ echo ;\
+ echo Running iwl-enable iwlagn...;\
+ /usr/sbin/iwl-enable iwlagn ;\
+ fi
+ @# If on distributions like Mandriva which like to
+ @# compress their modules this will find out and do
+ @# it for you. Reason is some old version of modutils
+ @# won't know mac80211.ko should be used instead of
+ @# mac80211.ko.gz
+ @./scripts/compress_modules
+ @# Mandrake doesn't have a depmod.d/ conf file to prefer
+ @# the updates/ dir which is what we use so we add one for it
+ @# (or any other distribution that doens't have this).
+ @./scripts/check_depmod
+ @# Udev stuff needed for the new compat_firmware_class.
+ @./compat/scripts/compat_firmware_install
+ @/sbin/depmod -a
+ @echo
+ @echo "Currently detected wireless subsystem modules:"
+ @echo
+ @$(MODPROBE) -l mac80211
+ @$(MODPROBE) -l cfg80211
+ @$(MODPROBE) -l lib80211
+ @$(MODPROBE) -l adm8211
+ @$(MODPROBE) -l ar9170usb
+ @$(MODPROBE) -l at76c50x-usb
+ @$(MODPROBE) -l ath
+ @$(MODPROBE) -l ath5k
+ @$(MODPROBE) -l ath6kl
+ @$(MODPROBE) -l ath9k
+ @$(MODPROBE) -l ath9k_htc
+ @$(MODPROBE) -l b43
+ @$(MODPROBE) -l b43legacy
+ @$(MODPROBE) -l b44
+ @$(MODPROBE) -l carl9170
+ @$(MODPROBE) -l brcm80211
+ @$(MODPROBE) -l cdc_ether
+ @$(MODPROBE) -l eeprom_93cx6
+ @$(MODPROBE) -l ipw2100
+ @$(MODPROBE) -l ipw2200
+ @$(MODPROBE) -l iwl3945
+ @$(MODPROBE) -l iwlagn
+ @$(MODPROBE) -l iwlcore
+ @$(MODPROBE) -l iwmc3200wifi
+ @$(MODPROBE) -l lib80211_crypt_ccmp
+ @$(MODPROBE) -l lib80211_crypt_tkip
+ @$(MODPROBE) -l lib80211_crypt_wep
+ @$(MODPROBE) -l libertas
+ @$(MODPROBE) -l libertas_cs
+ @$(MODPROBE) -l libertas_sdio
+ @$(MODPROBE) -l libertas_spi
+ @$(MODPROBE) -l libertas_tf
+ @$(MODPROBE) -l libertas_tf_usb
+ @$(MODPROBE) -l libipw
+ @$(MODPROBE) -l mac80211_hwsim
+ @$(MODPROBE) -l mwl8k
+ @$(MODPROBE) -l orinoco_cs
+ @$(MODPROBE) -l orinoco_nortel
+ @$(MODPROBE) -l orinoco_pci
+ @$(MODPROBE) -l orinoco_plx
+ @$(MODPROBE) -l orinoco_tld
+ @$(MODPROBE) -l orinoco_usb
+ @$(MODPROBE) -l orinoco
+ @$(MODPROBE) -l p54common
+ @$(MODPROBE) -l p54pci
+ @$(MODPROBE) -l p54spi
+ @$(MODPROBE) -l p54usb
+ @$(MODPROBE) -l rndis_host
+ @$(MODPROBE) -l rndis_wlan
+ @$(MODPROBE) -l rt2400pci
+ @$(MODPROBE) -l rt2500pci
+ @$(MODPROBE) -l rt2500usb
+ @$(MODPROBE) -l rt2800pci
+ @$(MODPROBE) -l rt2800usb
+ @$(MODPROBE) -l rt2x00lib
+ @$(MODPROBE) -l rt2x00pci
+ @$(MODPROBE) -l rt2x00usb
+ @$(MODPROBE) -l rt61pci
+ @$(MODPROBE) -l rt73usb
+ @$(MODPROBE) -l rtl8180
+ @$(MODPROBE) -l rtl8187
+ @$(MODPROBE) -l rtlwifi
+ @$(MODPROBE) -l rtl8192ce
+ @$(MODPROBE) -l spectrum_cs
+ @$(MODPROBE) -l ssb
+ @$(MODPROBE) -l usb8xxx
+ @$(MODPROBE) -l usbnet
+ @$(MODPROBE) -l wl1251
+ @$(MODPROBE) -l wl12xx
+ @$(MODPROBE) -l zd1211rw
+ @echo
+ @echo "Currently detected ethernet subsystem modules:"
+ @echo
+ @$(MODPROBE) -l atl1
+ @$(MODPROBE) -l atl2
+ @$(MODPROBE) -l atl1e
+ @$(MODPROBE) -l atl1c
+ @echo
+ @echo "Currently detected bluetooth subsystem modules:"
+ @echo
+ @$(MODPROBE) -l ath3k
+ @$(MODPROBE) -l bcm203x
+ @$(MODPROBE) -l bluecard_cs
+ @$(MODPROBE) -l bluetooth
+ @$(MODPROBE) -l bnep
+ @$(MODPROBE) -l bpa10x
+ @$(MODPROBE) -l bt3c_cs
+ @$(MODPROBE) -l btmrvl
+ @$(MODPROBE) -l btmrvl_sdio
+ @$(MODPROBE) -l btsdio
+ @$(MODPROBE) -l btusb
+ @$(MODPROBE) -l btuart_cs
+ @$(MODPROBE) -l cmtp
+ @$(MODPROBE) -l dtl1_cs
+ @$(MODPROBE) -l hidp
+ @$(MODPROBE) -l hci_vhci
+ @$(MODPROBE) -l hci_uart
+ @$(MODPROBE) -l l2cap
+ @$(MODPROBE) -l rfcomm
+ @$(MODPROBE) -l sco
+ @echo
+ @echo Now run:
+ @echo
+ @echo sudo make unload to unload all: wireless, bluetooth and ethernet modules
+ @echo sudo make wlunload to unload wireless modules
+ @echo sudo make btunload to unload bluetooth modules
+ @echo
+ @echo Run sudo modprobe 'driver-name' to load your desired driver.
+ @echo If unsure reboot.
+ @echo
+
+uninstall:
+ @# New location, matches upstream
+ @rm -rf $(KLIB)/$(KMODDIR)/compat/
+ @rm -rf $(KLIB)/$(KMODDIR)/net/mac80211/
+ @rm -rf $(KLIB)/$(KMODDIR)/net/rfkill/
+ @rm -rf $(KLIB)/$(KMODDIR)/net/wireless/
+ @rm -rf $(KLIB)/$(KMODDIR)/drivers/ssb/
+ @rm -rf $(KLIB)/$(KMODDIR)/drivers/net/usb/
+ @rm -rf $(KLIB)/$(KMODDIR)/drivers/net/wireless/
+ @rm -rf $(KLIB)/$(KMODDIR)/drivers/staging/
+ @rm -rf $(KLIB)/$(KMODDIR)/drivers/net/atl*
+ @rm -rf $(KLIB)/$(KMODDIR)/drivers/net/alx.ko*
+ @# Lets only remove the stuff we are sure we are providing
+ @# on the misc directory.
+ @rm -f $(KLIB)/$(KMODDIR)/drivers/misc/eeprom/eeprom_93cx6.ko*
+ @rm -f $(KLIB)/$(KMODDIR)/drivers/misc/eeprom_93cx6.ko*
+ @rm -f $(KLIB)/$(KMODDIR)/drivers/net/b44.ko*
+ @/sbin/depmod -a
+ @echo
+ @echo "Your old wireless subsystem modules were left intact:"
+ @echo
+ @$(MODPROBE) -l mac80211
+ @$(MODPROBE) -l cfg80211
+ @$(MODPROBE) -l lib80211
+ @$(MODPROBE) -l adm8211
+ @$(MODPROBE) -l ar9170usb
+ @$(MODPROBE) -l at76c50x-usb
+ @$(MODPROBE) -l ath
+ @$(MODPROBE) -l ath5k
+ @$(MODPROBE) -l ath6kl
+ @$(MODPROBE) -l ath9k
+ @$(MODPROBE) -l ath9k_htc
+ @$(MODPROBE) -l b43
+ @$(MODPROBE) -l b43legacy
+ @$(MODPROBE) -l b44
+ @$(MODPROBE) -l carl9170
+ @$(MODPROBE) -l brcm80211
+ @$(MODPROBE) -l cdc_ether
+ @$(MODPROBE) -l eeprom_93cx6
+ @$(MODPROBE) -l ipw2100
+ @$(MODPROBE) -l ipw2200
+ @$(MODPROBE) -l iwl3945
+ @$(MODPROBE) -l iwlagn
+ @$(MODPROBE) -l iwlcore
+ @$(MODPROBE) -l iwmc3200wifi
+ @$(MODPROBE) -l lib80211_crypt_ccmp
+ @$(MODPROBE) -l lib80211_crypt_tkip
+ @$(MODPROBE) -l lib80211_crypt_wep
+ @$(MODPROBE) -l libertas
+ @$(MODPROBE) -l libertas_cs
+ @$(MODPROBE) -l libertas_sdio
+ @$(MODPROBE) -l libertas_spi
+ @$(MODPROBE) -l libertas_tf
+ @$(MODPROBE) -l libertas_tf_usb
+ @$(MODPROBE) -l libipw
+ @$(MODPROBE) -l mac80211_hwsim
+ @$(MODPROBE) -l mwl8k
+ @$(MODPROBE) -l orinoco_cs
+ @$(MODPROBE) -l orinoco_nortel
+ @$(MODPROBE) -l orinoco_pci
+ @$(MODPROBE) -l orinoco_plx
+ @$(MODPROBE) -l orinoco_tld
+ @$(MODPROBE) -l orinoco_usb
+ @$(MODPROBE) -l orinoco
+ @$(MODPROBE) -l p54common
+ @$(MODPROBE) -l p54pci
+ @$(MODPROBE) -l p54spi
+ @$(MODPROBE) -l p54usb
+ @$(MODPROBE) -l rndis_host
+ @$(MODPROBE) -l rndis_wlan
+ @$(MODPROBE) -l rt2400pci
+ @$(MODPROBE) -l rt2500pci
+ @$(MODPROBE) -l rt2500usb
+ @$(MODPROBE) -l rt2800pci
+ @$(MODPROBE) -l rt2800usb
+ @$(MODPROBE) -l rt2x00lib
+ @$(MODPROBE) -l rt2x00pci
+ @$(MODPROBE) -l rt2x00usb
+ @$(MODPROBE) -l rt61pci
+ @$(MODPROBE) -l rt73usb
+ @$(MODPROBE) -l rtl8180
+ @$(MODPROBE) -l rtl8187
+ @$(MODPROBE) -l rtlwifi
+ @$(MODPROBE) -l rtl8192ce
+ @$(MODPROBE) -l spectrum_cs
+ @$(MODPROBE) -l ssb
+ @$(MODPROBE) -l usb8xxx
+ @$(MODPROBE) -l usbnet
+ @$(MODPROBE) -l wl1251
+ @$(MODPROBE) -l wl12xx
+ @$(MODPROBE) -l zd1211rw
+ @echo
+ @echo "Your old ethernet subsystem modules are left intact:"
+ @echo
+ @$(MODPROBE) -l atl1
+ @$(MODPROBE) -l atl2
+ @$(MODPROBE) -l atl1e
+ @$(MODPROBE) -l atl1c
+ @$(MODPORBE) -l alx
+ @echo
+ @echo "Your old bluetooth subsystem modules were left intact:"
+ @echo
+ @$(MODPROBE) -l ath3k
+ @$(MODPROBE) -l bcm203x
+ @$(MODPROBE) -l bluecard_cs
+ @$(MODPROBE) -l bluetooth
+ @$(MODPROBE) -l bnep
+ @$(MODPROBE) -l bpa10x
+ @$(MODPROBE) -l bt3c_cs
+ @$(MODPROBE) -l btmrvl
+ @$(MODPROBE) -l btmrvl_sdio
+ @$(MODPROBE) -l btsdio
+ @$(MODPROBE) -l btusb
+ @$(MODPROBE) -l btuart_cs
+ @$(MODPROBE) -l cmtp
+ @$(MODPROBE) -l dtl1_cs
+ @$(MODPROBE) -l hidp
+ @$(MODPROBE) -l hci_vhci
+ @$(MODPROBE) -l hci_uart
+ @$(MODPROBE) -l l2cap
+ @$(MODPROBE) -l rfcomm
+ @$(MODPROBE) -l sco
+ @
+ @echo
+
+clean:
+ @if [ -d net -a -d $(KLIB_BUILD) ]; then \
+ $(MAKE) -C $(KLIB_BUILD) M=$(PWD) clean ;\
+ fi
+ @rm -f $(CREL_PRE)*
+unload:
+ @./scripts/unload.sh
+
+btunload:
+ @./scripts/btunload.sh
+
+wlunload:
+ @./scripts/wlunload.sh
+
+
+.PHONY: all clean install uninstall unload btunload wlunload modules bt
+
+endif
+
+clean-files += Module.symvers Module.markers modules modules.order
+clean-files += $(CREL_CHECK) $(CONFIG_CHECK)
diff --git a/README b/README
new file mode 100644
index 0000000..09d8612
--- /dev/null
+++ b/README
@@ -0,0 +1,417 @@
+
+Linux Wireless compatibility package
+=====================================
+
+This is a Linux wireless compatibility package which provides the latest
+Linux wireless subsystem enhancements for kernels 2.6.24 and above.
+It is technically possible to support kernels < 2.6.24 but more
+work is required for that.
+It also provides Linux bluetooth subsystem enhancements for kernels 2.6.27 and above.
+
+With a local git repository you can update the compatibility package yourself.
+For more information on how to do this please refer the Developers section below.
+
+Documentation
+------------
+
+This package is also documented online and has more-up-to date
+information online than on this README file. You should read the wiki page
+and not rely on this README:
+
+http://wireless.kernel.org/en/users/Download
+
+Subscribe to the wiki page to get updates on the documentation.
+
+Where to get the latest
+-----------------------
+
+This package lets you build your own 'latest', all you need is a local git
+repository. The process is documented in the Developers section of this document.
+However since not many users are expected to keep a local git repository we
+provide daily snapshots of this package + the wireless and bluetooth subsystem
+code. You can find the latest snapshot at:
+
+linux-next.git version:
+http://wireless.kernel.org/en/users/Download
+
+stable version:
+http://wireless.kernel.org/en/users/Download/stable/
+
+Versions
+--------
+
+There are two different versions of this package available.
+ * Version based on latest linux-next.git tree named compat-wireless-YYYY-MM-DD.tar.bz2
+ * Version based on linux-2.6-stable.git tree named compat-wireless-2.6.CC.DD.tar.bz2
+
+Both versions should work for every kernel > 2.6.24. The differences are the
+code they are containing. The version based on linux-next.git contains the
+wireless and bluetooth subsystem out of linux-next.git and the stable version
+the wireless subsystem out of the corresponding linux stable version.
+
+Selecting your driver
+---------------------
+
+If you know the driver you want you can select it with our
+helper script:
+
+./scripts/driver-select
+
+Run that script to see more information.
+Not all drivers are currently enabled via driver-select.
+
+Building, and installing
+------------------------
+
+Build: compile the latest linux wireless subsystem
+
+ make
+
+Install:
+
+We use the updates/ directory so your distribution's drivers are left intact.
+
+ sudo make install
+
+Uninstall:
+
+This nukes our changes to updates/ so you can go back to using your
+distribution's supported drivers.
+
+ sudo make uninstall
+
+Load:
+
+Reboot unless you know what you are doing.
+(Usually modprobe 'driver-name' is the way to go)
+
+
+Bluetooth modules can be separately compiled and installed using below commands
+Bluetooth drivers are also available via ./scripts/driver-select
+
+Build:
+ make bt
+
+Install:
+ sudo make btinstall
+
+Uninstall:
+ sudo make btuninstall
+
+Unload:
+ sudo make btunload
+
+Load:
+ modprobe driver-name. If unsure, reboot.
+
+Drivers
+-------
+
+This is the list of drivers the package currently provides. It adds
+all new drivers or drivers which keep being updated which you might
+be interested in.
+
+Drivers list:
+
+adm8211
+ar9170usb
+at76c50x-usb
+ath5k
+ath6kl
+ath9k
+ath9k_htc
+b43
+b43legacy
+b44
+carl9170
+brcm80211
+ipw2100
+ipw2200
+iwl3945
+iwlagn
+iwlcore
+iwmc3200wifi
+libertas
+libertas_cs
+libertas_sdio
+libertas_spi
+libertas_tf
+libertas_tf_usb
+libipw
+mwl8k
+orinoco_cs
+orinoco_nortel
+orinoco_pci
+orinoco_plx
+orinoco_tld
+orinoco_usb
+orinoco
+p54common
+p54pci
+p54spi
+p54usb
+rt2400pci
+rt2500pci
+rt2500usb
+rt2800pci
+rt2800usb
+rt61pci
+rt73usb
+rtl8180
+rtl8187
+rtlwifi
+rtl8192ce
+spectrum_cs
+ssb
+wl1251
+wl12xx
+zd1211rw
+
+This package also provides more drivers which may be documented here
+
+For a complete list see:
+
+http://wireless.kernel.org/en/users/Download
+
+Non-wireless drivers
+--------------------
+
+Atheros Ethernet drivers:
+
+atl1
+atl1c
+atl1e
+atl2
+
+To support b43 ssb is also provided, and since ssb is also provided
+we provide b44 (the ethernet driver).
+
+The new rfkill drivers also provided and backported.
+
+Bluetooth drivers:
+
+ath3k
+bcm203x
+bluecard_cs
+bluetooth
+bnep
+bpa10x
+bt3c_cs
+btmrvl
+btmrvl_sdio
+btsdio
+btusb
+btuart_cs
+cmtp
+dtl1_cs
+hidp
+hci_vhci
+hci_uart
+l2cap
+rfcomm
+sco
+
+Firmware:
+---------
+
+If your driver needs firmware please be sure to check the driver page
+for that driver here:
+
+http://wireless.kernel.org/en/users/Drivers
+
+Why?
+----
+
+For users or developers stuck on older kernels that want to help test or
+patch wireless work. Additionally if you're on a recent kernel this lets
+you get the latest and greatest linux-next git work without much effort.
+This may mean new drivers for some users. Last but not least we hope this
+will encourage vendors and developers to post patches upstream first
+rather than forking or maintaining their own mac80211 releases with
+their own patches for their own drivers.
+
+Building for external kernels
+----------------------------------
+
+If you have a kernel you do not have installed but yet want to build the
+compat-wireless-2.6 drivers for it you can use this syntax:
+
+make KLIB=/home/mcgrof/kernels/linux-2.6.23.9 KLIB_BUILD=/home/mcgrof/kernels/linux-2.6.23.9
+
+If you have a kernel installed, which is not your currently running kernel (e.g. via
+distro updates; plus its corresponding kernel-dev package), you can use this syntax:
+
+make KLIB=/lib/modules/2.6.30.6-53.fc11.x86_64
+
+ and to install to your system's root path for the non-running kernel:
+
+make KLIB=/lib/modules/2.6.30.6-53.fc11.x86_64 KMODPATH_ARG='INSTALL_MOD_PATH=' install
+
+Bugs
+-----
+
+If you've found a bug please report it to our linux-wireless mailing list:
+
+linux-wireless@vger.kernel.org
+
+Report the bug if you are working with the latest and greatest.
+If your bug is compatibility-related then we should still try to fix
+it within the compat.[ch] work.
+
+ChangeLog
+---------
+
+Here you see the list of changes to all wireless drivers, the wireless core and mac80211.
+
+http://git.kernel.org/?p=linux/kernel/git/linville/wireless-testing.git;a=log;
+
+This views all the changes on wireless-testing.git.
+
+License
+-------
+
+This work is a subset of the Linux kernel as such we keep the kernel's
+Copyright practice. Some files have their own copyright and in those
+cases the license is mentioned in the file. All additional work made
+to building this package is licensed under the GPLv2.
+
+Developers
+----------
+
+Compatibility work goes into compat/compat.[ch]. If using those files do
+not suffice additional actual code changes can go into patches/*.patch.
+
+An extended and more up to date version can be found at:
+
+http://wireless.kernel.org/en/users/Download/hacking
+
+This section deals with development details of compat-wireless and the other
+trees it uses. If you want to make your own compat-wireless tarballs, or if you
+see something busted with compat-wireless or just want to add something new or
+an enhancement this is the guide for you.
+
+Git trees you will need
+-----------------------
+
+compat-wireless backports both the bluetooth and 802.11 subsystems down to older
+kernels. To be able to synchronize backporting the latest and greatest the
+linux-next.git tree is used as its main source for kernel updates. General Linux
+kernel compatibility is addressed through a general kernel compatibility tree,
+compat.git. compat-wireless then has its own tree for specific wireless
+compatibility. You will then need to checkout three trees to start hacking on
+compat-wireless:
+
+git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
+git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/compat.git
+git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/compat-wireless-2.6.git
+
+Linux next
+----------
+
+The linux-next.git tree brings all subsystems being worked on for the next
+kernel release into one tree. So if the current rc kernel is 2.6.33-rc5, this
+means linux-next will have what people today are working on for the 2.6.34
+kernel release.
+
+compat.git
+----------
+
+The compat git tree is a general kernel compatibility layer which can be shared
+amongst different compatibility projects, or drivers. compat-wireless is just
+one of the kernel compatibility projects using compat.git. compat.git builds a
+general compatibility module, compat, and any additional modules to let you get
+new general kernel updates from future kernels on your old kernels.
+
+compat.git modules
+------------------
+
+compat.git provides a few modules and headers to help with general kernel
+compatibility.
+
+compat
+------
+
+Provides all exported symbols implemented in each respective kernel
+compat-2.6.xy.c files. Upon module load it just initializes the Linux kernel's
+''power management Quality Of Service'' (aka '''pm-qos''') Interface interface
+added as of the 2.6.24 kernel. No other things are initialized, the rest of the
+compat module just acts as a library of exported symbols.
+
+compat_firmware_class
+---------------------
+
+Another module which compat.git provides is a backport of the firmware_class
+module which got updated recently newer with a new request_firmware_nowait()
+to allow better asynchronous firmware uploading. This was added as of the 2.6.33
+kernel. The firmware_class module has been backported into a new module called
+compat_firmware_class. A separate module has been defined instead of a direct
+replacement for firmware_class since your system may have old drivers which use
+the old request_firmware_nowait() and would bust if they used the new
+request_firmware_nowait(). The compat_firmware_class module registers its own
+sysfs subsystem and as such also gets udev events sent through a separate
+subsystem. Because of this a new udev rules file is required and provided.
+
+compat-wireless.git
+-------------------
+
+Anything that is not general kernel compatibility but instead specific to 802.11
+or bluetooth goes into compat-wireless.git. After you've cloned all three trees,
+linux-next.git, compat.git and compat-wireless.git you need to change into the
+compat-wireless directory and tell compat-wireless where you linux-next and
+compat.git trees are. You do this with environment variables GIT_TREE and
+GIT_COMPAT_TREE. You can do for example:
+
+export GIT_TREE=/home/user/wireless-testing/
+export GIT_COMPAT_TREE=/home/users/compat.git/
+
+Then you can update your local sources based on these linux-next.git and
+compat.git trees:
+
+scripts/admin-clean.sh - Cleans the compat-wireless-2.6 tree
+scripts/admin-update.sh - Updates compat-wireless-2.6 with your git tree
+scripts/admin-refresh.sh - Does the above two
+
+Adding new drivers
+------------------
+
+Most new drivers are enabled for compilation. If see a driver you would like
+enabled try it into the mix, test them and if they work enable them and send
+the respective patches.
+
+Sending patches
+---------------
+
+Remember there are three trees. linux-next itself is a conglomeration of kernel
+git trees itself, so patches for linux-next.git should be sent to each
+respective subsystem for which the patches are targeted for. So for example for
+802.11 you will want to send them to John Linville and cc linux-wireless, for
+further guidelines on this see the Submitting Patches guidelines for 802.11.
+http://wireless.kernel.org/en/developers/Documentation/SubmittingPatches
+As another example, for bluetooth you will want to send them to Marcel
+Holtmann and cc the linux-bluetooth mailing list. If your patch touches on
+others areas of the kernel refer to the MAINTAINERS file on the kernel.
+
+For compat.git and compat-wireless.git please send patches against to:
+
+To: Luis R. Rodriguez <mcgrof@kernel.org>
+CC: linux-wireless@vger.kernel.org, linux-bluetooth@vger.kernel.org
+Subject: [PATCH] compat-2.6: fix foo
+
+For patches for compat.git please use a subject like the following:
+
+Subject: [PATCH] compat: fix foo
+
+For compat-wireless.git please use a subject like the following:
+
+Subject: [PATCH] compat-wireless: fix foo
+
+Patches are preferred sent with a clear commit log entry, if unfamiliar with
+how to send patches please refer to
+http://wireless.kernel.org/en/developers/Documentation/git-guide.
+
+
+TODO
+-----
+ * Dialog (make menuconfig) option for this package
+ * Compatibility work for 2.6.18 --> 2.6.24
+
diff --git a/code-metrics.txt b/code-metrics.txt
new file mode 100644
index 0000000..8fe4dcf
--- /dev/null
+++ b/code-metrics.txt
@@ -0,0 +1,14 @@
+
+[36mcompat-wireless code metrics[00m
+
+[35m 825441[00m - Total upstream lines of code being pulled
+[01;32m 2418[00m - backport code changes
+[01;32m 2087[00m - backport code additions
+[01;32m 331[00m - backport code deletions
+[01;32m 9096[00m - backport from compat module
+[01;32m 11514[00m - total backport code
+[31m 1.3949[00m - % of code consists of backport work
+
+Base tree: [01;32math6kl.git[00m
+Base tree version: [35mbranching-rel-3.2.3-74-g39f7005[00m
+compat-wireless release: [01;33m3.3-OSR-2012-10-11-15-g2bd3ebf[00m
diff --git a/compat/Makefile b/compat/Makefile
new file mode 100644
index 0000000..f4f4493
--- /dev/null
+++ b/compat/Makefile
@@ -0,0 +1,96 @@
+ifndef OLDJENKINS
+obj-m += wireless_compat.o
+
+obj-$(CONFIG_COMPAT_FIRMWARE_CLASS) += compat_firmware_class.o
+
+wireless_compat-y += main.o
+
+# Compat kernel compatibility code
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_14) += compat-2.6.14.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_18) += compat-2.6.18.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_19) += compat-2.6.19.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_21) += compat-2.6.21.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_22) += compat-2.6.22.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_23) += compat-2.6.23.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_24) += compat-2.6.24.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_25) += \
+ compat-2.6.25.o \
+ pm_qos_params.o
+
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_26) += compat-2.6.26.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_27) += compat-2.6.27.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_28) += compat-2.6.28.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_29) += compat-2.6.29.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_32) += compat-2.6.32.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_33) += compat-2.6.33.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_35) += compat-2.6.35.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_36) += compat-2.6.36.o
+
+wireless_compat-$(CONFIG_COMPAT_KFIFO) += kfifo.o
+
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_37) += compat-2.6.37.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_38) += compat-2.6.38.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_39) += \
+ compat-2.6.39.o \
+ kstrtox.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_3_0) += compat-3.0.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_3_2) += compat-3.2.o
+
+wireless_compat-$(CONFIG_COMPAT_CORDIC) += cordic.o
+wireless_compat-$(CONFIG_COMPAT_CRC8) += crc8.o
+
+ifndef CONFIG_64BIT
+ifndef CONFIG_GENERIC_ATOMIC64
+ wireless_compat-y += compat_atomic.o
+endif
+endif
+
+else
+obj-m += compat.o
+
+#compat-objs :=
+
+obj-$(CONFIG_COMPAT_FIRMWARE_CLASS) += compat_firmware_class.o
+
+compat-y += main.o
+
+# Compat kernel compatibility code
+compat-$(CONFIG_COMPAT_KERNEL_2_6_14) += compat-2.6.14.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_18) += compat-2.6.18.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_19) += compat-2.6.19.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_21) += compat-2.6.21.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_22) += compat-2.6.22.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_23) += compat-2.6.23.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_24) += compat-2.6.24.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_25) += \
+ compat-2.6.25.o \
+ pm_qos_params.o
+
+compat-$(CONFIG_COMPAT_KERNEL_2_6_26) += compat-2.6.26.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_27) += compat-2.6.27.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_28) += compat-2.6.28.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_29) += compat-2.6.29.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_32) += compat-2.6.32.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_33) += compat-2.6.33.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_35) += compat-2.6.35.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_36) += compat-2.6.36.o
+
+compat-$(CONFIG_COMPAT_KFIFO) += kfifo.o
+
+compat-$(CONFIG_COMPAT_KERNEL_2_6_37) += compat-2.6.37.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_38) += compat-2.6.38.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_39) += \
+ compat-2.6.39.o \
+ kstrtox.o
+compat-$(CONFIG_COMPAT_KERNEL_3_0) += compat-3.0.o
+compat-$(CONFIG_COMPAT_KERNEL_3_2) += compat-3.2.o
+
+compat-$(CONFIG_COMPAT_CORDIC) += cordic.o
+compat-$(CONFIG_COMPAT_CRC8) += crc8.o
+
+ifndef CONFIG_64BIT
+ifndef CONFIG_GENERIC_ATOMIC64
+ compat-y += compat_atomic.o
+endif
+endif
+endif
diff --git a/compat/compat-2.6.14.c b/compat/compat-2.6.14.c
new file mode 100644
index 0000000..3de847d
--- /dev/null
+++ b/compat/compat-2.6.14.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.14.
+ */
+
+#include <net/compat.h>
+
+/* 2.6.14 compat code goes here */
+
diff --git a/compat/compat-2.6.18.c b/compat/compat-2.6.18.c
new file mode 100644
index 0000000..c7961ee
--- /dev/null
+++ b/compat/compat-2.6.18.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.18.
+ */
+
+#include <net/compat.h>
+
+/* 2.6.18 compat code goes here */
+
diff --git a/compat/compat-2.6.19.c b/compat/compat-2.6.19.c
new file mode 100644
index 0000000..60c3404
--- /dev/null
+++ b/compat/compat-2.6.19.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.19.
+ */
+
+#include <net/compat.h>
+
+/* 2.6.19 compat code goes here */
+
diff --git a/compat/compat-2.6.21.c b/compat/compat-2.6.21.c
new file mode 100644
index 0000000..7cf8861
--- /dev/null
+++ b/compat/compat-2.6.21.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.21.
+ */
+
+#include <net/compat.h>
+
+/* 2.6.21 compat code goes here */
+
diff --git a/compat/compat-2.6.22.c b/compat/compat-2.6.22.c
new file mode 100644
index 0000000..d4df7b7
--- /dev/null
+++ b/compat/compat-2.6.22.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.22.
+ */
+
+#include <net/compat.h>
+
+/* 2.6.22 compat code goes here */
+
diff --git a/compat/compat-2.6.23.c b/compat/compat-2.6.23.c
new file mode 100644
index 0000000..67d0075
--- /dev/null
+++ b/compat/compat-2.6.23.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.23.
+ */
+
+#include <net/compat.h>
+
+/* On net/core/dev.c as of 2.6.24 */
+int __dev_addr_delete(struct dev_addr_list **list, int *count,
+ void *addr, int alen, int glbl)
+{
+ struct dev_addr_list *da;
+
+ for (; (da = *list) != NULL; list = &da->next) {
+ if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
+ alen == da->da_addrlen) {
+ if (glbl) {
+ int old_glbl = da->da_gusers;
+ da->da_gusers = 0;
+ if (old_glbl == 0)
+ break;
+ }
+ if (--da->da_users)
+ return 0;
+
+ *list = da->next;
+ kfree(da);
+ (*count)--;
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+EXPORT_SYMBOL(__dev_addr_delete);
+
+/* On net/core/dev.c as of 2.6.24. This is not yet used by mac80211 but
+ * might as well add it */
+int __dev_addr_add(struct dev_addr_list **list, int *count,
+ void *addr, int alen, int glbl)
+{
+ struct dev_addr_list *da;
+
+ for (da = *list; da != NULL; da = da->next) {
+ if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
+ da->da_addrlen == alen) {
+ if (glbl) {
+ int old_glbl = da->da_gusers;
+ da->da_gusers = 1;
+ if (old_glbl)
+ return 0;
+ }
+ da->da_users++;
+ return 0;
+ }
+ }
+
+ da = kmalloc(sizeof(*da), GFP_ATOMIC);
+ if (da == NULL)
+ return -ENOMEM;
+ memcpy(da->da_addr, addr, alen);
+ da->da_addrlen = alen;
+ da->da_users = 1;
+ da->da_gusers = glbl ? 1 : 0;
+ da->next = *list;
+ *list = da;
+ (*count)++;
+ return 0;
+}
+EXPORT_SYMBOL(__dev_addr_add);
+
+
+/* Part of net/core/dev_mcast.c as of 2.6.23. This is a slightly different version.
+ * Since da->da_synced is not part of 2.6.22 we need to take longer route when
+ * syncing */
+
+/**
+ * dev_mc_sync - Synchronize device's multicast list to another device
+ * @to: destination device
+ * @from: source device
+ *
+ * Add newly added addresses to the destination device and release
+ * addresses that have no users left. The source device must be
+ * locked by netif_tx_lock_bh.
+ *
+ * This function is intended to be called from the dev->set_multicast_list
+ * function of layered software devices.
+ */
+int dev_mc_sync(struct net_device *to, struct net_device *from)
+{
+ struct dev_addr_list *da, *next, *da_to;
+ int err = 0;
+
+ netif_tx_lock_bh(to);
+ da = from->mc_list;
+ while (da != NULL) {
+ int synced = 0;
+ next = da->next;
+ da_to = to->mc_list;
+ /* 2.6.22 does not have da->da_synced so lets take the long route */
+ while (da_to != NULL) {
+ if (memcmp(da_to->da_addr, da->da_addr, da_to->da_addrlen) == 0 &&
+ da->da_addrlen == da_to->da_addrlen)
+ synced = 1;
+ break;
+ }
+ if (!synced) {
+ err = __dev_addr_add(&to->mc_list, &to->mc_count,
+ da->da_addr, da->da_addrlen, 0);
+ if (err < 0)
+ break;
+ da->da_users++;
+ } else if (da->da_users == 1) {
+ __dev_addr_delete(&to->mc_list, &to->mc_count,
+ da->da_addr, da->da_addrlen, 0);
+ __dev_addr_delete(&from->mc_list, &from->mc_count,
+ da->da_addr, da->da_addrlen, 0);
+ }
+ da = next;
+ }
+ if (!err)
+ __dev_set_rx_mode(to);
+ netif_tx_unlock_bh(to);
+
+ return err;
+}
+EXPORT_SYMBOL(dev_mc_sync);
+
+
+/* Part of net/core/dev_mcast.c as of 2.6.23. This is a slighty different version.
+ * Since da->da_synced is not part of 2.6.22 we need to take longer route when
+ * unsyncing */
+
+/**
+ * dev_mc_unsync - Remove synchronized addresses from the destination
+ * device
+ * @to: destination device
+ * @from: source device
+ *
+ * Remove all addresses that were added to the destination device by
+ * dev_mc_sync(). This function is intended to be called from the
+ * dev->stop function of layered software devices.
+ */
+void dev_mc_unsync(struct net_device *to, struct net_device *from)
+{
+ struct dev_addr_list *da, *next, *da_to;
+
+ netif_tx_lock_bh(from);
+ netif_tx_lock_bh(to);
+
+ da = from->mc_list;
+ while (da != NULL) {
+ bool synced = false;
+ next = da->next;
+ da_to = to->mc_list;
+ /* 2.6.22 does not have da->da_synced so lets take the long route */
+ while (da_to != NULL) {
+ if (memcmp(da_to->da_addr, da->da_addr, da_to->da_addrlen) == 0 &&
+ da->da_addrlen == da_to->da_addrlen)
+ synced = true;
+ break;
+ }
+ if (!synced) {
+ da = next;
+ continue;
+ }
+ __dev_addr_delete(&to->mc_list, &to->mc_count,
+ da->da_addr, da->da_addrlen, 0);
+ __dev_addr_delete(&from->mc_list, &from->mc_count,
+ da->da_addr, da->da_addrlen, 0);
+ da = next;
+ }
+ __dev_set_rx_mode(to);
+
+ netif_tx_unlock_bh(to);
+ netif_tx_unlock_bh(from);
+}
+EXPORT_SYMBOL(dev_mc_unsync);
+
+/* Added as of 2.6.23 on net/core/dev.c. Slightly modifed, no dev->set_rx_mode on
+ * 2.6.22 so ignore that. */
+
+/*
+ * Upload unicast and multicast address lists to device and
+ * configure RX filtering. When the device doesn't support unicast
+ * filtering it is put in promiscous mode while unicast addresses
+ * are present.
+ */
+void __dev_set_rx_mode(struct net_device *dev)
+{
+ /* dev_open will call this function so the list will stay sane. */
+ if (!(dev->flags&IFF_UP))
+ return;
+
+ if (!netif_device_present(dev))
+ return;
+
+/* This needs to be ported to 2.6.22 framework */
+#if 0
+ /* Unicast addresses changes may only happen under the rtnl,
+ * therefore calling __dev_set_promiscuity here is safe.
+ */
+ if (dev->uc_count > 0 && !dev->uc_promisc) {
+ __dev_set_promiscuity(dev, 1);
+ dev->uc_promisc = 1;
+ } else if (dev->uc_count == 0 && dev->uc_promisc) {
+ __dev_set_promiscuity(dev, -1);
+ dev->uc_promisc = 0;
+ }
+#endif
+
+ if (dev->set_multicast_list)
+ dev->set_multicast_list(dev);
+}
+
+/**
+ * pci_try_set_mwi - enables memory-write-invalidate PCI transaction
+ * @dev: the PCI device for which MWI is enabled
+ *
+ * Enables the Memory-Write-Invalidate transaction in %PCI_COMMAND.
+ * Callers are not required to check the return value.
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+int pci_try_set_mwi(struct pci_dev *dev)
+{
+ int rc = 0;
+#ifdef HAVE_PCI_SET_MWI
+ rc = pci_set_mwi(dev);
+#endif
+ return rc;
+}
+EXPORT_SYMBOL(pci_try_set_mwi);
+#endif
+
diff --git a/compat/compat-2.6.24.c b/compat/compat-2.6.24.c
new file mode 100644
index 0000000..b9ae16b
--- /dev/null
+++ b/compat/compat-2.6.24.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.24.
+ */
+
+#include <net/compat.h>
+#include <net/arp.h>
+
+/*
+ * We simply won't use it though, just declare it for our wrappers and
+ * for usage with tons of code that makes mention to it.
+ */
+struct net init_net;
+EXPORT_SYMBOL(init_net);
+
+/* 2.6.22 and 2.6.23 have eth_header_cache_update defined as extern in include/linux/etherdevice.h
+ * and actually defined in net/ethernet/eth.c but 2.6.24 exports it. Lets export it here */
+
+/**
+ * eth_header_cache_update - update cache entry
+ * @hh: destination cache entry
+ * @dev: network device
+ * @haddr: new hardware address
+ *
+ * Called by Address Resolution module to notify changes in address.
+ */
+void eth_header_cache_update(struct hh_cache *hh,
+ struct net_device *dev,
+ unsigned char *haddr)
+{
+ memcpy(((u8 *) hh->hh_data) + HH_DATA_OFF(sizeof(struct ethhdr)),
+ haddr, ETH_ALEN);
+}
+EXPORT_SYMBOL(eth_header_cache_update);
+
+/* 2.6.22 and 2.6.23 have eth_header_cache defined as extern in include/linux/etherdevice.h
+ * and actually defined in net/ethernet/eth.c but 2.6.24 exports it. Lets export it here */
+
+/**
+ * eth_header_cache - fill cache entry from neighbour
+ * @neigh: source neighbour
+ * @hh: destination cache entry
+ * Create an Ethernet header template from the neighbour.
+ */
+int eth_header_cache(struct neighbour *neigh, struct hh_cache *hh)
+{
+ __be16 type = hh->hh_type;
+ struct ethhdr *eth;
+ const struct net_device *dev = neigh->dev;
+
+ eth = (struct ethhdr *)
+ (((u8 *) hh->hh_data) + (HH_DATA_OFF(sizeof(*eth))));
+
+ if (type == htons(ETH_P_802_3))
+ return -1;
+
+ eth->h_proto = type;
+ memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
+ memcpy(eth->h_dest, neigh->ha, ETH_ALEN);
+ hh->hh_len = ETH_HLEN;
+ return 0;
+}
+EXPORT_SYMBOL(eth_header_cache);
+
+/* 2.6.22 and 2.6.23 have eth_header() defined as extern in include/linux/etherdevice.h
+ * and actually defined in net/ethernet/eth.c but 2.6.24 exports it. Lets export it here */
+
+/**
+ * eth_header - create the Ethernet header
+ * @skb: buffer to alter
+ * @dev: source device
+ * @type: Ethernet type field
+ * @daddr: destination address (NULL leave destination address)
+ * @saddr: source address (NULL use device source address)
+ * @len: packet length (<= skb->len)
+ *
+ *
+ * Set the protocol type. For a packet of type ETH_P_802_3 we put the length
+ * in here instead. It is up to the 802.2 layer to carry protocol information.
+ */
+int eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
+ void *daddr, void *saddr, unsigned len)
+{
+ struct ethhdr *eth = (struct ethhdr *)skb_push(skb, ETH_HLEN);
+
+ if (type != ETH_P_802_3)
+ eth->h_proto = htons(type);
+ else
+ eth->h_proto = htons(len);
+
+ /*
+ * Set the source hardware address.
+ */
+
+ if (!saddr)
+ saddr = dev->dev_addr;
+ memcpy(eth->h_source, saddr, dev->addr_len);
+
+ if (daddr) {
+ memcpy(eth->h_dest, daddr, dev->addr_len);
+ return ETH_HLEN;
+ }
+
+ /*
+ * Anyway, the loopback-device should never use this function...
+ */
+
+ if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
+ memset(eth->h_dest, 0, dev->addr_len);
+ return ETH_HLEN;
+ }
+
+ return -ETH_HLEN;
+}
+
+EXPORT_SYMBOL(eth_header);
+
+/* 2.6.22 and 2.6.23 have eth_rebuild_header defined as extern in include/linux/etherdevice.h
+ * and actually defined in net/ethernet/eth.c but 2.6.24 exports it. Lets export it here */
+
+/**
+ * eth_rebuild_header- rebuild the Ethernet MAC header.
+ * @skb: socket buffer to update
+ *
+ * This is called after an ARP or IPV6 ndisc it's resolution on this
+ * sk_buff. We now let protocol (ARP) fill in the other fields.
+ *
+ * This routine CANNOT use cached dst->neigh!
+ * Really, it is used only when dst->neigh is wrong.
+ */
+int eth_rebuild_header(struct sk_buff *skb)
+{
+ struct ethhdr *eth = (struct ethhdr *)skb->data;
+ struct net_device *dev = skb->dev;
+
+ switch (eth->h_proto) {
+#ifdef CONFIG_INET
+ case __constant_htons(ETH_P_IP):
+ return arp_find(eth->h_dest, skb);
+#endif
+ default:
+ printk(KERN_DEBUG
+ "%s: unable to resolve type %X addresses.\n",
+ dev->name, (int)eth->h_proto);
+
+ memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(eth_rebuild_header);
+
diff --git a/compat/compat-2.6.25.c b/compat/compat-2.6.25.c
new file mode 100644
index 0000000..7cb996b
--- /dev/null
+++ b/compat/compat-2.6.25.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2007-2010 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.25.
+ */
+
+#include <linux/miscdevice.h>
+
+/**
+ * The following things are out of ./lib/vsprintf.c
+ * The new iwlwifi driver is using them.
+ */
+
+/**
+ * strict_strtoul - convert a string to an unsigned long strictly
+ * @cp: The string to be converted
+ * @base: The number base to use
+ * @res: The converted result value
+ *
+ * strict_strtoul converts a string to an unsigned long only if the
+ * string is really an unsigned long string, any string containing
+ * any invalid char at the tail will be rejected and -EINVAL is returned,
+ * only a newline char at the tail is acceptible because people generally
+ * change a module parameter in the following way:
+ *
+ * echo 1024 > /sys/module/e1000/parameters/copybreak
+ *
+ * echo will append a newline to the tail.
+ *
+ * It returns 0 if conversion is successful and *res is set to the converted
+ * value, otherwise it returns -EINVAL and *res is set to 0.
+ *
+ * simple_strtoul just ignores the successive invalid characters and
+ * return the converted value of prefix part of the string.
+ */
+int strict_strtoul(const char *cp, unsigned int base, unsigned long *res);
+
+/**
+ * strict_strtol - convert a string to a long strictly
+ * @cp: The string to be converted
+ * @base: The number base to use
+ * @res: The converted result value
+ *
+ * strict_strtol is similiar to strict_strtoul, but it allows the first
+ * character of a string is '-'.
+ *
+ * It returns 0 if conversion is successful and *res is set to the converted
+ * value, otherwise it returns -EINVAL and *res is set to 0.
+ */
+int strict_strtol(const char *cp, unsigned int base, long *res);
+
+#define define_strict_strtoux(type, valtype) \
+int strict_strtou##type(const char *cp, unsigned int base, valtype *res)\
+{ \
+ char *tail; \
+ valtype val; \
+ size_t len; \
+ \
+ *res = 0; \
+ len = strlen(cp); \
+ if (len == 0) \
+ return -EINVAL; \
+ \
+ val = simple_strtou##type(cp, &tail, base); \
+ if ((*tail == '\0') || \
+ ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {\
+ *res = val; \
+ return 0; \
+ } \
+ \
+ return -EINVAL; \
+} \
+
+#define define_strict_strtox(type, valtype) \
+int strict_strto##type(const char *cp, unsigned int base, valtype *res) \
+{ \
+ int ret; \
+ if (*cp == '-') { \
+ ret = strict_strtou##type(cp+1, base, res); \
+ if (!ret) \
+ *res = -(*res); \
+ } else \
+ ret = strict_strtou##type(cp, base, res); \
+ \
+ return ret; \
+} \
+
+define_strict_strtoux(l, unsigned long)
+define_strict_strtox(l, long)
+
+EXPORT_SYMBOL(strict_strtoul);
+EXPORT_SYMBOL(strict_strtol);
+
diff --git a/compat/compat-2.6.26.c b/compat/compat-2.6.26.c
new file mode 100644
index 0000000..f471506
--- /dev/null
+++ b/compat/compat-2.6.26.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2007-2010 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.26.
+ *
+ * Copyright holders from ported work:
+ *
+ * Copyright (c) 2002-2003 Patrick Mochel <mochel@osdl.org>
+ * Copyright (c) 2006-2007 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (c) 2006-2007 Novell Inc.
+ */
+
+#include <net/compat.h>
+
+/* 2.6.24 does not have the struct kobject with a name */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25))
+
+/**
+ * kobject_set_name_vargs - Set the name of an kobject
+ * @kobj: struct kobject to set the name of
+ * @fmt: format string used to build the name
+ * @vargs: vargs to format the string.
+ */
+static
+int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
+ va_list vargs)
+{
+ const char *old_name = kobj->name;
+ char *s;
+
+ if (kobj->name && !fmt)
+ return 0;
+
+ kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
+ if (!kobj->name)
+ return -ENOMEM;
+
+ /* ewww... some of these buggers have '/' in the name ... */
+ while ((s = strchr(kobj->name, '/')))
+ s[0] = '!';
+
+ kfree(old_name);
+ return 0;
+}
+#else
+static
+int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
+ va_list vargs)
+{
+ struct device *dev;
+ unsigned int len;
+ va_list aq;
+
+ dev = container_of(kobj, struct device, kobj);
+
+ va_copy(aq, vargs);
+ len = vsnprintf(NULL, 0, fmt, aq);
+ va_end(aq);
+
+ len = len < BUS_ID_SIZE ? (len + 1) : BUS_ID_SIZE;
+
+ vsnprintf(dev->bus_id, len, fmt, vargs);
+ return 0;
+}
+#endif
+
+/**
+ * dev_set_name - set a device name
+ * @dev: device
+ * @fmt: format string for the device's name
+ */
+int dev_set_name(struct device *dev, const char *fmt, ...)
+{
+ va_list vargs;
+ int err;
+
+ va_start(vargs, fmt);
+ err = kobject_set_name_vargs(&dev->kobj, fmt, vargs);
+ va_end(vargs);
+ return err;
+}
+EXPORT_SYMBOL_GPL(dev_set_name);
+
diff --git a/compat/compat-2.6.27.c b/compat/compat-2.6.27.c
new file mode 100644
index 0000000..25bee28
--- /dev/null
+++ b/compat/compat-2.6.27.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.27
+ */
+
+#include <linux/compat.h>
+#include <linux/pci.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24))
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#endif
+
+/* rfkill notification chain */
+#define RFKILL_STATE_CHANGED 0x0001 /* state of a normal rfkill
+ switch has changed */
+
+/*
+ * e5899e1b7d73e67de758a32174a859cc2586c0b9 made pci_pme_capable() external,
+ * it was defined internally, some drivers want access to this information.
+ *
+ * Unfortunately the old kernels do not have ->pm_cap or ->pme_support so
+ * we have to call the PCI routines directly.
+ */
+
+/**
+ * pci_pme_capable - check the capability of PCI device to generate PME#
+ * @dev: PCI device to handle.
+ * @state: PCI state from which device will issue PME#.
+ *
+ * This is the backport code for older kernels for compat-wireless, we read stuff
+ * from the initialization stuff from pci_pm_init().
+ */
+bool pci_pme_capable(struct pci_dev *dev, pci_power_t state)
+{
+ int pm;
+ u16 pmc = 0;
+ u16 pme_support; /* as from the pci dev */
+ /* find PCI PM capability in list */
+ pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+ if (!pm)
+ return false;
+
+ if ((pmc & PCI_PM_CAP_VER_MASK) > 3) {
+ dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n",
+ pmc & PCI_PM_CAP_VER_MASK);
+ return false;
+ }
+
+ pmc &= PCI_PM_CAP_PME_MASK;
+
+ if (!pmc)
+ return false;
+
+ pme_support = pmc >> PCI_PM_CAP_PME_SHIFT;
+
+ /* Check device's ability to generate PME# */
+
+ return !!(pme_support & (1 << state));
+}
+EXPORT_SYMBOL(pci_pme_capable);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24))
+/**
+ * mmc_align_data_size - pads a transfer size to a more optimal value
+ * @card: the MMC card associated with the data transfer
+ * @sz: original transfer size
+ *
+ * Pads the original data size with a number of extra bytes in
+ * order to avoid controller bugs and/or performance hits
+ * (e.g. some controllers revert to PIO for certain sizes).
+ *
+ * Returns the improved size, which might be unmodified.
+ *
+ * Note that this function is only relevant when issuing a
+ * single scatter gather entry.
+ */
+unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz)
+{
+ /*
+ * FIXME: We don't have a system for the controller to tell
+ * the core about its problems yet, so for now we just 32-bit
+ * align the size.
+ */
+ sz = ((sz + 3) / 4) * 4;
+
+ return sz;
+}
+EXPORT_SYMBOL(mmc_align_data_size);
+
+/*
+ * Calculate the maximum byte mode transfer size
+ */
+static inline unsigned int sdio_max_byte_size(struct sdio_func *func)
+{
+ unsigned int mval = (unsigned int) min(func->card->host->max_seg_size,
+ func->card->host->max_blk_size);
+ mval = min(mval, func->max_blksize);
+ return min(mval, 512u); /* maximum size for byte mode */
+}
+
+/**
+ * sdio_align_size - pads a transfer size to a more optimal value
+ * @func: SDIO function
+ * @sz: original transfer size
+ *
+ * Pads the original data size with a number of extra bytes in
+ * order to avoid controller bugs and/or performance hits
+ * (e.g. some controllers revert to PIO for certain sizes).
+ *
+ * If possible, it will also adjust the size so that it can be
+ * handled in just a single request.
+ *
+ * Returns the improved size, which might be unmodified.
+ */
+unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz)
+{
+ unsigned int orig_sz;
+ unsigned int blk_sz, byte_sz;
+ unsigned chunk_sz;
+
+ orig_sz = sz;
+
+ /*
+ * Do a first check with the controller, in case it
+ * wants to increase the size up to a point where it
+ * might need more than one block.
+ */
+ sz = mmc_align_data_size(func->card, sz);
+
+ /*
+ * If we can still do this with just a byte transfer, then
+ * we're done.
+ */
+ if (sz <= sdio_max_byte_size(func))
+ return sz;
+
+ if (func->card->cccr.multi_block) {
+ /*
+ * Check if the transfer is already block aligned
+ */
+ if ((sz % func->cur_blksize) == 0)
+ return sz;
+
+ /*
+ * Realign it so that it can be done with one request,
+ * and recheck if the controller still likes it.
+ */
+ blk_sz = ((sz + func->cur_blksize - 1) /
+ func->cur_blksize) * func->cur_blksize;
+ blk_sz = mmc_align_data_size(func->card, blk_sz);
+
+ /*
+ * This value is only good if it is still just
+ * one request.
+ */
+ if ((blk_sz % func->cur_blksize) == 0)
+ return blk_sz;
+
+ /*
+ * We failed to do one request, but at least try to
+ * pad the remainder properly.
+ */
+ byte_sz = mmc_align_data_size(func->card,
+ sz % func->cur_blksize);
+ if (byte_sz <= sdio_max_byte_size(func)) {
+ blk_sz = sz / func->cur_blksize;
+ return blk_sz * func->cur_blksize + byte_sz;
+ }
+ } else {
+ /*
+ * We need multiple requests, so first check that the
+ * controller can handle the chunk size;
+ */
+ chunk_sz = mmc_align_data_size(func->card,
+ sdio_max_byte_size(func));
+ if (chunk_sz == sdio_max_byte_size(func)) {
+ /*
+ * Fix up the size of the remainder (if any)
+ */
+ byte_sz = orig_sz % chunk_sz;
+ if (byte_sz) {
+ byte_sz = mmc_align_data_size(func->card,
+ byte_sz);
+ }
+
+ return (orig_sz / chunk_sz) * chunk_sz + byte_sz;
+ }
+ }
+
+ /*
+ * The controller is simply incapable of transferring the size
+ * we want in decent manner, so just return the original size.
+ */
+ return orig_sz;
+}
+EXPORT_SYMBOL_GPL(sdio_align_size);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) */
+
+#ifdef CONFIG_DEBUG_FS
+/*
+ * Backport of debugfs_remove_recursive() without using the internals globals
+ * which are used by the kernel's version with:
+ * simple_release_fs(&debugfs_mount, &debugfs_mount_count);
+ */
+void debugfs_remove_recursive(struct dentry *dentry)
+{
+ struct dentry *last = NULL;
+
+ /* Sanity checks */
+ if (!dentry || !dentry->d_parent || !dentry->d_parent->d_inode)
+ return;
+
+ while (dentry != last) {
+ struct dentry *child = dentry;
+
+ /* Find a child without children */
+ while (!list_empty(&child->d_subdirs))
+ child = list_entry(child->d_subdirs.next,
+ struct dentry,
+ d_u.d_child);
+
+ /* Bail out if we already tried to remove that entry */
+ if (child == last)
+ return;
+
+ last = child;
+ debugfs_remove(child);
+ }
+}
+EXPORT_SYMBOL_GPL(debugfs_remove_recursive);
+#endif /* CONFIG_DEBUG_FS */
+
diff --git a/compat/compat-2.6.28.c b/compat/compat-2.6.28.c
new file mode 100644
index 0000000..72c9e09
--- /dev/null
+++ b/compat/compat-2.6.28.c
@@ -0,0 +1,463 @@
+/*
+ * Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.28.
+ */
+
+#include <linux/compat.h>
+#include <linux/usb.h>
+#include <linux/tty.h>
+#include <asm/poll.h>
+
+/* 2.6.28 compat code goes here */
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23))
+#if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE)
+/*
+ * Compat-wireless notes for USB backport stuff:
+ *
+ * urb->reject exists on 2.6.27, the poison/unpoison helpers
+ * did not though. The anchor poison does not exist so we cannot use them.
+ *
+ * USB anchor poising seems to exist to prevent future driver sumbissions
+ * of usb_anchor_urb() to an anchor marked as poisoned. For older kernels
+ * we cannot use that, so new usb_anchor_urb()s will be anchored. The down
+ * side to this should be submission of URBs will continue being anchored
+ * on an anchor instead of having them being rejected immediately when the
+ * driver realized we needed to stop. For ar9170 we poison URBs upon the
+ * ar9170 mac80211 stop callback(), don't think this should be so bad.
+ * It mean there is period of time in older kernels for which we continue
+ * to anchor new URBs to a known stopped anchor. We have two anchors
+ * (TX, and RX)
+ */
+
+#if 0
+/**
+ * usb_poison_urb - reliably kill a transfer and prevent further use of an URB
+ * @urb: pointer to URB describing a previously submitted request,
+ * may be NULL
+ *
+ * This routine cancels an in-progress request. It is guaranteed that
+ * upon return all completion handlers will have finished and the URB
+ * will be totally idle and cannot be reused. These features make
+ * this an ideal way to stop I/O in a disconnect() callback.
+ * If the request has not already finished or been unlinked
+ * the completion handler will see urb->status == -ENOENT.
+ *
+ * After and while the routine runs, attempts to resubmit the URB will fail
+ * with error -EPERM. Thus even if the URB's completion handler always
+ * tries to resubmit, it will not succeed and the URB will become idle.
+ *
+ * This routine may not be used in an interrupt context (such as a bottom
+ * half or a completion handler), or when holding a spinlock, or in other
+ * situations where the caller can't schedule().
+ *
+ * This routine should not be called by a driver after its disconnect
+ * method has returned.
+ */
+void usb_poison_urb(struct urb *urb)
+{
+ might_sleep();
+ if (!(urb && urb->dev && urb->ep))
+ return;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
+ spin_lock_irq(&usb_reject_lock);
+#endif
+ ++urb->reject;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
+ spin_unlock_irq(&usb_reject_lock);
+#endif
+ /*
+ * XXX: usb_hcd_unlink_urb() needs backporting... this is defined
+ * on usb hcd.c but urb.c gets access to it. That is, older kernels
+ * have usb_hcd_unlink_urb() but its not exported, nor can we
+ * re-implement it exactly. This essentially dequeues the urb from
+ * hw, we need to figure out a way to backport this.
+ */
+ //usb_hcd_unlink_urb(urb, -ENOENT);
+
+ wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
+}
+EXPORT_SYMBOL_GPL(usb_poison_urb);
+#endif
+#endif /* CONFIG_USB */
+
+#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
+
+#include <pcmcia/ds.h>
+struct pcmcia_cfg_mem {
+ tuple_t tuple;
+ cisparse_t parse;
+ u8 buf[256];
+ cistpl_cftable_entry_t dflt;
+};
+/**
+ * pcmcia_loop_config() - loop over configuration options
+ * @p_dev: the struct pcmcia_device which we need to loop for.
+ * @conf_check: function to call for each configuration option.
+ * It gets passed the struct pcmcia_device, the CIS data
+ * describing the configuration option, and private data
+ * being passed to pcmcia_loop_config()
+ * @priv_data: private data to be passed to the conf_check function.
+ *
+ * pcmcia_loop_config() loops over all configuration options, and calls
+ * the driver-specific conf_check() for each one, checking whether
+ * it is a valid one. Returns 0 on success or errorcode otherwise.
+ */
+int pcmcia_loop_config(struct pcmcia_device *p_dev,
+ int (*conf_check) (struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data),
+ void *priv_data)
+{
+ struct pcmcia_cfg_mem *cfg_mem;
+
+ tuple_t *tuple;
+ int ret;
+ unsigned int vcc;
+
+ cfg_mem = kzalloc(sizeof(struct pcmcia_cfg_mem), GFP_KERNEL);
+ if (cfg_mem == NULL)
+ return -ENOMEM;
+
+ /* get the current Vcc setting */
+ vcc = p_dev->socket->socket.Vcc;
+
+ tuple = &cfg_mem->tuple;
+ tuple->TupleData = cfg_mem->buf;
+ tuple->TupleDataMax = 255;
+ tuple->TupleOffset = 0;
+ tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ tuple->Attributes = 0;
+
+ ret = pcmcia_get_first_tuple(p_dev, tuple);
+ while (!ret) {
+ cistpl_cftable_entry_t *cfg = &cfg_mem->parse.cftable_entry;
+
+ if (pcmcia_get_tuple_data(p_dev, tuple))
+ goto next_entry;
+
+ if (pcmcia_parse_tuple(tuple, &cfg_mem->parse))
+ goto next_entry;
+
+ /* default values */
+ p_dev->conf.ConfigIndex = cfg->index;
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+ cfg_mem->dflt = *cfg;
+
+ ret = conf_check(p_dev, cfg, &cfg_mem->dflt, vcc, priv_data);
+ if (!ret)
+ break;
+
+next_entry:
+ ret = pcmcia_get_next_tuple(p_dev, tuple);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(pcmcia_loop_config);
+
+#endif /* CONFIG_PCMCIA */
+
+#if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE)
+
+void usb_unpoison_urb(struct urb *urb)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
+ unsigned long flags;
+#endif
+
+ if (!urb)
+ return;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
+ spin_lock_irqsave(&usb_reject_lock, flags);
+#endif
+ --urb->reject;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
+ spin_unlock_irqrestore(&usb_reject_lock, flags);
+#endif
+}
+EXPORT_SYMBOL_GPL(usb_unpoison_urb);
+
+
+#if 0
+/**
+ * usb_poison_anchored_urbs - cease all traffic from an anchor
+ * @anchor: anchor the requests are bound to
+ *
+ * this allows all outstanding URBs to be poisoned starting
+ * from the back of the queue. Newly added URBs will also be
+ * poisoned
+ *
+ * This routine should not be called by a driver after its disconnect
+ * method has returned.
+ */
+void usb_poison_anchored_urbs(struct usb_anchor *anchor)
+{
+ struct urb *victim;
+
+ spin_lock_irq(&anchor->lock);
+ // anchor->poisoned = 1; /* XXX: Cannot backport */
+ while (!list_empty(&anchor->urb_list)) {
+ victim = list_entry(anchor->urb_list.prev, struct urb,
+ anchor_list);
+ /* we must make sure the URB isn't freed before we kill it*/
+ usb_get_urb(victim);
+ spin_unlock_irq(&anchor->lock);
+ /* this will unanchor the URB */
+ usb_poison_urb(victim);
+ usb_put_urb(victim);
+ spin_lock_irq(&anchor->lock);
+ }
+ spin_unlock_irq(&anchor->lock);
+}
+EXPORT_SYMBOL_GPL(usb_poison_anchored_urbs);
+#endif
+
+/**
+ * usb_anchor_empty - is an anchor empty
+ * @anchor: the anchor you want to query
+ *
+ * returns 1 if the anchor has no urbs associated with it
+ */
+int usb_anchor_empty(struct usb_anchor *anchor)
+{
+ return list_empty(&anchor->urb_list);
+}
+
+EXPORT_SYMBOL_GPL(usb_anchor_empty);
+#endif /* CONFIG_USB */
+#endif
+
+void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar)
+{
+ /*
+ * Make sure the BAR is actually a memory resource, not an IO resource
+ */
+ if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
+ WARN_ON(1);
+ return NULL;
+ }
+ return ioremap_nocache(pci_resource_start(pdev, bar),
+ pci_resource_len(pdev, bar));
+}
+EXPORT_SYMBOL_GPL(pci_ioremap_bar);
+
+static unsigned long round_jiffies_common(unsigned long j, int cpu,
+ bool force_up)
+{
+ int rem;
+ unsigned long original = j;
+
+ /*
+ * We don't want all cpus firing their timers at once hitting the
+ * same lock or cachelines, so we skew each extra cpu with an extra
+ * 3 jiffies. This 3 jiffies came originally from the mm/ code which
+ * already did this.
+ * The skew is done by adding 3*cpunr, then round, then subtract this
+ * extra offset again.
+ */
+ j += cpu * 3;
+
+ rem = j % HZ;
+
+ /*
+ * If the target jiffie is just after a whole second (which can happen
+ * due to delays of the timer irq, long irq off times etc etc) then
+ * we should round down to the whole second, not up. Use 1/4th second
+ * as cutoff for this rounding as an extreme upper bound for this.
+ * But never round down if @force_up is set.
+ */
+ if (rem < HZ/4 && !force_up) /* round down */
+ j = j - rem;
+ else /* round up */
+ j = j - rem + HZ;
+
+ /* now that we have rounded, subtract the extra skew again */
+ j -= cpu * 3;
+
+ if (j <= jiffies) /* rounding ate our timeout entirely; */
+ return original;
+ return j;
+}
+
+/**
+ * round_jiffies_up - function to round jiffies up to a full second
+ * @j: the time in (absolute) jiffies that should be rounded
+ *
+ * This is the same as round_jiffies() except that it will never
+ * round down. This is useful for timeouts for which the exact time
+ * of firing does not matter too much, as long as they don't fire too
+ * early.
+ */
+unsigned long round_jiffies_up(unsigned long j)
+{
+ return round_jiffies_common(j, raw_smp_processor_id(), true);
+}
+EXPORT_SYMBOL_GPL(round_jiffies_up);
+
+void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off,
+ int size)
+{
+ skb_fill_page_desc(skb, i, page, off, size);
+ skb->len += size;
+ skb->data_len += size;
+ skb->truesize += size;
+}
+EXPORT_SYMBOL(skb_add_rx_frag);
+
+void tty_write_unlock(struct tty_struct *tty)
+{
+ mutex_unlock(&tty->atomic_write_lock);
+ wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
+}
+
+int tty_write_lock(struct tty_struct *tty, int ndelay)
+{
+ if (!mutex_trylock(&tty->atomic_write_lock)) {
+ if (ndelay)
+ return -EAGAIN;
+ if (mutex_lock_interruptible(&tty->atomic_write_lock))
+ return -ERESTARTSYS;
+ }
+ return 0;
+}
+
+/**
+ * send_prio_char - send priority character
+ *
+ * Send a high priority character to the tty even if stopped
+ *
+ * Locking: none for xchar method, write ordering for write method.
+ */
+
+static int send_prio_char(struct tty_struct *tty, char ch)
+{
+ int was_stopped = tty->stopped;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
+ if (tty->ops->send_xchar) {
+ tty->ops->send_xchar(tty, ch);
+#else
+ if (tty->driver->send_xchar) {
+ tty->driver->send_xchar(tty, ch);
+#endif
+ return 0;
+ }
+
+ if (tty_write_lock(tty, 0) < 0)
+ return -ERESTARTSYS;
+
+ if (was_stopped)
+ start_tty(tty);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
+ tty->ops->write(tty, &ch, 1);
+#else
+ tty->driver->write(tty, &ch, 1);
+#endif
+ if (was_stopped)
+ stop_tty(tty);
+ tty_write_unlock(tty);
+ return 0;
+}
+
+int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
+ unsigned long flags;
+#endif
+ int retval;
+
+ switch (cmd) {
+ case TCXONC:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ switch (arg) {
+ case TCOOFF:
+ if (!tty->flow_stopped) {
+ tty->flow_stopped = 1;
+ stop_tty(tty);
+ }
+ break;
+ case TCOON:
+ if (tty->flow_stopped) {
+ tty->flow_stopped = 0;
+ start_tty(tty);
+ }
+ break;
+ case TCIOFF:
+ if (STOP_CHAR(tty) != __DISABLED_CHAR)
+ return send_prio_char(tty, STOP_CHAR(tty));
+ break;
+ case TCION:
+ if (START_CHAR(tty) != __DISABLED_CHAR)
+ return send_prio_char(tty, START_CHAR(tty));
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+ case TCFLSH:
+ return tty_perform_flush(tty, arg);
+ case TIOCPKT:
+ {
+ int pktmode;
+
+ if (tty->driver->type != TTY_DRIVER_TYPE_PTY ||
+ tty->driver->subtype != PTY_TYPE_MASTER)
+ return -ENOTTY;
+ if (get_user(pktmode, (int __user *) arg))
+ return -EFAULT;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
+ spin_lock_irqsave(&tty->ctrl_lock, flags);
+#endif
+ if (pktmode) {
+ if (!tty->packet) {
+ tty->packet = 1;
+ tty->link->ctrl_status = 0;
+ }
+ } else
+ tty->packet = 0;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
+ spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+#endif
+ return 0;
+ }
+ default:
+ /* Try the mode commands */
+ return tty_mode_ioctl(tty, file, cmd, arg);
+ }
+}
+EXPORT_SYMBOL(n_tty_ioctl_helper);
+
+/**
+ * pci_wake_from_d3 - enable/disable device to wake up from D3_hot or D3_cold
+ * @dev: PCI device to prepare
+ * @enable: True to enable wake-up event generation; false to disable
+ *
+ * Many drivers want the device to wake up the system from D3_hot or D3_cold
+ * and this function allows them to set that up cleanly - pci_enable_wake()
+ * should not be called twice in a row to enable wake-up due to PCI PM vs ACPI
+ * ordering constraints.
+ *
+ * This function only returns error code if the device is not capable of
+ * generating PME# from both D3_hot and D3_cold, and the platform is unable to
+ * enable wake-up power for it.
+ */
+int pci_wake_from_d3(struct pci_dev *dev, bool enable)
+{
+ return pci_pme_capable(dev, PCI_D3cold) ?
+ pci_enable_wake(dev, PCI_D3cold, enable) :
+ pci_enable_wake(dev, PCI_D3hot, enable);
+}
+EXPORT_SYMBOL(pci_wake_from_d3);
+
diff --git a/compat/compat-2.6.29.c b/compat/compat-2.6.29.c
new file mode 100644
index 0000000..14f5e90
--- /dev/null
+++ b/compat/compat-2.6.29.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2007-2010 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.29.
+ */
+
+#include <linux/compat.h>
+#include <linux/usb.h>
+#include <linux/etherdevice.h>
+
+/*
+ * If you don't see your net_device_ops implemented on
+ * netdev_attach_ops() then you are shit out of luck and
+ * you must do the nasty ifdef magic, unless you figure
+ * out a way to squeze your hacks into this routine :)
+ */
+void netdev_attach_ops(struct net_device *dev,
+ const struct net_device_ops *ops)
+{
+ dev->open = ops->ndo_open;
+ dev->init = ops->ndo_init;
+ dev->stop = ops->ndo_stop;
+ dev->hard_start_xmit = ops->ndo_start_xmit;
+ dev->change_rx_flags = ops->ndo_change_rx_flags;
+ dev->set_multicast_list = ops->ndo_set_multicast_list;
+ dev->validate_addr = ops->ndo_validate_addr;
+ dev->do_ioctl = ops->ndo_do_ioctl;
+ dev->set_config = ops->ndo_set_config;
+ dev->change_mtu = ops->ndo_change_mtu;
+ dev->set_mac_address = ops->ndo_set_mac_address;
+ dev->tx_timeout = ops->ndo_tx_timeout;
+ if (ops->ndo_get_stats)
+ dev->get_stats = ops->ndo_get_stats;
+ dev->vlan_rx_register = ops->ndo_vlan_rx_register;
+ dev->vlan_rx_add_vid = ops->ndo_vlan_rx_add_vid;
+ dev->vlan_rx_kill_vid = ops->ndo_vlan_rx_kill_vid;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = ops->ndo_poll_controller;
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
+ dev->select_queue = ops->ndo_select_queue;
+#endif
+}
+EXPORT_SYMBOL(netdev_attach_ops);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23))
+#if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE)
+/**
+ * usb_unpoison_anchored_urbs - let an anchor be used successfully again
+ * @anchor: anchor the requests are bound to
+ *
+ * Reverses the effect of usb_poison_anchored_urbs
+ * the anchor can be used normally after it returns
+ */
+void usb_unpoison_anchored_urbs(struct usb_anchor *anchor)
+{
+ unsigned long flags;
+ struct urb *lazarus;
+
+ spin_lock_irqsave(&anchor->lock, flags);
+ list_for_each_entry(lazarus, &anchor->urb_list, anchor_list) {
+ usb_unpoison_urb(lazarus);
+ }
+ //anchor->poisoned = 0; /* XXX: cannot backport */
+ spin_unlock_irqrestore(&anchor->lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs);
+#endif /* CONFIG_USB */
+#endif
+
+/**
+ * eth_mac_addr - set new Ethernet hardware address
+ * @dev: network device
+ * @p: socket address
+ * Change hardware address of device.
+ *
+ * This doesn't change hardware matching, so needs to be overridden
+ * for most real devices.
+ */
+int eth_mac_addr(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+
+ if (netif_running(dev))
+ return -EBUSY;
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+ memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+ return 0;
+}
+EXPORT_SYMBOL(eth_mac_addr);
+
+/**
+ * eth_change_mtu - set new MTU size
+ * @dev: network device
+ * @new_mtu: new Maximum Transfer Unit
+ *
+ * Allow changing MTU size. Needs to be overridden for devices
+ * supporting jumbo frames.
+ */
+int eth_change_mtu(struct net_device *dev, int new_mtu)
+{
+ if (new_mtu < 68 || new_mtu > ETH_DATA_LEN)
+ return -EINVAL;
+ dev->mtu = new_mtu;
+ return 0;
+}
+EXPORT_SYMBOL(eth_change_mtu);
+
+int eth_validate_addr(struct net_device *dev)
+{
+ if (!is_valid_ether_addr(dev->dev_addr))
+ return -EADDRNOTAVAIL;
+
+ return 0;
+}
+EXPORT_SYMBOL(eth_validate_addr);
+/* Source: net/ethernet/eth.c */
+
+#define NETREG_DUMMY 5
+/**
+ * init_dummy_netdev - init a dummy network device for NAPI
+ * @dev: device to init
+ *
+ * This takes a network device structure and initialize the minimum
+ * amount of fields so it can be used to schedule NAPI polls without
+ * registering a full blown interface. This is to be used by drivers
+ * that need to tie several hardware interfaces to a single NAPI
+ * poll scheduler due to HW limitations.
+ */
+int init_dummy_netdev(struct net_device *dev)
+{
+ /* Clear everything. Note we don't initialize spinlocks
+ * are they aren't supposed to be taken by any of the
+ * NAPI code and this dummy netdev is supposed to be
+ * only ever used for NAPI polls
+ */
+ memset(dev, 0, sizeof(struct net_device));
+
+ /* make sure we BUG if trying to hit standard
+ * register/unregister code path
+ */
+ dev->reg_state = NETREG_DUMMY;
+
+ /* initialize the ref count */
+ atomic_set(&dev->refcnt, 1);
+
+#ifdef CONFIG_NETPOLL
+ /* NAPI wants this */
+ INIT_LIST_HEAD(&dev->napi_list);
+#endif
+
+ /* a dummy interface is started by default */
+ set_bit(__LINK_STATE_PRESENT, &dev->state);
+ set_bit(__LINK_STATE_START, &dev->state);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(init_dummy_netdev);
+/* Source: net/core/dev.c */
+
diff --git a/compat/compat-2.6.32.c b/compat/compat-2.6.32.c
new file mode 100644
index 0000000..0b3af07
--- /dev/null
+++ b/compat/compat-2.6.32.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.32.
+ */
+
+#include <linux/compat.h>
+#include <linux/netdevice.h>
+
+int __dev_addr_add(struct dev_addr_list **list, int *count,
+ void *addr, int alen, int glbl)
+{
+ struct dev_addr_list *da;
+
+ for (da = *list; da != NULL; da = da->next) {
+ if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
+ da->da_addrlen == alen) {
+ if (glbl) {
+ int old_glbl = da->da_gusers;
+ da->da_gusers = 1;
+ if (old_glbl)
+ return 0;
+ }
+ da->da_users++;
+ return 0;
+ }
+ }
+
+ da = kzalloc(sizeof(*da), GFP_ATOMIC);
+ if (da == NULL)
+ return -ENOMEM;
+ memcpy(da->da_addr, addr, alen);
+ da->da_addrlen = alen;
+ da->da_users = 1;
+ da->da_gusers = glbl ? 1 : 0;
+ da->next = *list;
+ *list = da;
+ (*count)++;
+ return 0;
+}
+
+int __dev_addr_delete(struct dev_addr_list **list, int *count,
+ void *addr, int alen, int glbl)
+{
+ struct dev_addr_list *da;
+
+ for (; (da = *list) != NULL; list = &da->next) {
+ if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
+ alen == da->da_addrlen) {
+ if (glbl) {
+ int old_glbl = da->da_gusers;
+ da->da_gusers = 0;
+ if (old_glbl == 0)
+ break;
+ }
+ if (--da->da_users)
+ return 0;
+
+ *list = da->next;
+ kfree(da);
+ (*count)--;
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+int __dev_addr_sync(struct dev_addr_list **to, int *to_count,
+ struct dev_addr_list **from, int *from_count)
+{
+ struct dev_addr_list *da, *next;
+ int err = 0;
+
+ da = *from;
+ while (da != NULL) {
+ next = da->next;
+ if (!da->da_synced) {
+ err = __dev_addr_add(to, to_count,
+ da->da_addr, da->da_addrlen, 0);
+ if (err < 0)
+ break;
+ da->da_synced = 1;
+ da->da_users++;
+ } else if (da->da_users == 1) {
+ __dev_addr_delete(to, to_count,
+ da->da_addr, da->da_addrlen, 0);
+ __dev_addr_delete(from, from_count,
+ da->da_addr, da->da_addrlen, 0);
+ }
+ da = next;
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(__dev_addr_sync);
+
+void __dev_addr_unsync(struct dev_addr_list **to, int *to_count,
+ struct dev_addr_list **from, int *from_count)
+{
+ struct dev_addr_list *da, *next;
+
+ da = *from;
+ while (da != NULL) {
+ next = da->next;
+ if (da->da_synced) {
+ __dev_addr_delete(to, to_count,
+ da->da_addr, da->da_addrlen, 0);
+ da->da_synced = 0;
+ __dev_addr_delete(from, from_count,
+ da->da_addr, da->da_addrlen, 0);
+ }
+ da = next;
+ }
+}
+EXPORT_SYMBOL_GPL(__dev_addr_unsync);
+
+/*
+ * Nonzero if YEAR is a leap year (every 4 years,
+ * except every 100th isn't, and every 400th is).
+ */
+static int __isleap(long year)
+{
+ return (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0);
+}
+
+/* do a mathdiv for long type */
+static long math_div(long a, long b)
+{
+ return a / b - (a % b < 0);
+}
+
+/* How many leap years between y1 and y2, y1 must less or equal to y2 */
+static long leaps_between(long y1, long y2)
+{
+ long leaps1 = math_div(y1 - 1, 4) - math_div(y1 - 1, 100)
+ + math_div(y1 - 1, 400);
+ long leaps2 = math_div(y2 - 1, 4) - math_div(y2 - 1, 100)
+ + math_div(y2 - 1, 400);
+ return leaps2 - leaps1;
+}
+
+/* How many days come before each month (0-12). */
+static const unsigned short __mon_yday[2][13] = {
+ /* Normal years. */
+ {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
+ /* Leap years. */
+ {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
+};
+
+#define SECS_PER_HOUR (60 * 60)
+#define SECS_PER_DAY (SECS_PER_HOUR * 24)
+
+/**
+ * time_to_tm - converts the calendar time to local broken-down time
+ *
+ * @totalsecs the number of seconds elapsed since 00:00:00 on January 1, 1970,
+ * Coordinated Universal Time (UTC).
+ * @offset offset seconds adding to totalsecs.
+ * @result pointer to struct tm variable to receive broken-down time
+ */
+void time_to_tm(time_t totalsecs, int offset, struct tm *result)
+{
+ long days, rem, y;
+ const unsigned short *ip;
+
+ days = totalsecs / SECS_PER_DAY;
+ rem = totalsecs % SECS_PER_DAY;
+ rem += offset;
+ while (rem < 0) {
+ rem += SECS_PER_DAY;
+ --days;
+ }
+ while (rem >= SECS_PER_DAY) {
+ rem -= SECS_PER_DAY;
+ ++days;
+ }
+
+ result->tm_hour = rem / SECS_PER_HOUR;
+ rem %= SECS_PER_HOUR;
+ result->tm_min = rem / 60;
+ result->tm_sec = rem % 60;
+
+ /* January 1, 1970 was a Thursday. */
+ result->tm_wday = (4 + days) % 7;
+ if (result->tm_wday < 0)
+ result->tm_wday += 7;
+
+ y = 1970;
+
+ while (days < 0 || days >= (__isleap(y) ? 366 : 365)) {
+ /* Guess a corrected year, assuming 365 days per year. */
+ long yg = y + math_div(days, 365);
+
+ /* Adjust DAYS and Y to match the guessed year. */
+ days -= (yg - y) * 365 + leaps_between(y, yg);
+ y = yg;
+ }
+
+ result->tm_year = y - 1900;
+
+ result->tm_yday = days;
+
+ ip = __mon_yday[__isleap(y)];
+ for (y = 11; days < ip[y]; y--)
+ continue;
+ days -= ip[y];
+
+ result->tm_mon = y;
+ result->tm_mday = days + 1;
+}
+EXPORT_SYMBOL(time_to_tm);
+/* source: kernel/time/timeconv.c*/
+
diff --git a/compat/compat-2.6.33.c b/compat/compat-2.6.33.c
new file mode 100644
index 0000000..c8e568d
--- /dev/null
+++ b/compat/compat-2.6.33.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2009 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.33.
+ */
+
+#include <linux/compat.h>
+#include <linux/autoconf.h>
+
+#if defined(CONFIG_PCCARD) || defined(CONFIG_PCCARD_MODULE)
+
+/**
+ * pccard_loop_tuple() - loop over tuples in the CIS
+ * @s: the struct pcmcia_socket where the card is inserted
+ * @function: the device function we loop for
+ * @code: which CIS code shall we look for?
+ * @parse: buffer where the tuple shall be parsed (or NULL, if no parse)
+ * @priv_data: private data to be passed to the loop_tuple function.
+ * @loop_tuple: function to call for each CIS entry of type @function. IT
+ * gets passed the raw tuple, the paresed tuple (if @parse is
+ * set) and @priv_data.
+ *
+ * pccard_loop_tuple() loops over all CIS entries of type @function, and
+ * calls the @loop_tuple function for each entry. If the call to @loop_tuple
+ * returns 0, the loop exits. Returns 0 on success or errorcode otherwise.
+ */
+int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function,
+ cisdata_t code, cisparse_t *parse, void *priv_data,
+ int (*loop_tuple) (tuple_t *tuple,
+ cisparse_t *parse,
+ void *priv_data))
+{
+ tuple_t tuple;
+ cisdata_t *buf;
+ int ret;
+
+ buf = kzalloc(256, GFP_KERNEL);
+ if (buf == NULL) {
+ dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
+ return -ENOMEM;
+ }
+
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = 255;
+ tuple.TupleOffset = 0;
+ tuple.DesiredTuple = code;
+ tuple.Attributes = 0;
+
+ ret = pccard_get_first_tuple(s, function, &tuple);
+ while (!ret) {
+ if (pccard_get_tuple_data(s, &tuple))
+ goto next_entry;
+
+ if (parse)
+ if (pcmcia_parse_tuple(&tuple, parse))
+ goto next_entry;
+
+ ret = loop_tuple(&tuple, parse, priv_data);
+ if (!ret)
+ break;
+
+next_entry:
+ ret = pccard_get_next_tuple(s, function, &tuple);
+ }
+
+ kfree(buf);
+ return ret;
+}
+EXPORT_SYMBOL(pccard_loop_tuple);
+/* Source: drivers/pcmcia/cistpl.c */
+
+#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
+
+struct pcmcia_loop_mem {
+ struct pcmcia_device *p_dev;
+ void *priv_data;
+ int (*loop_tuple) (struct pcmcia_device *p_dev,
+ tuple_t *tuple,
+ void *priv_data);
+};
+
+/**
+ * pcmcia_do_loop_tuple() - internal helper for pcmcia_loop_config()
+ *
+ * pcmcia_do_loop_tuple() is the internal callback for the call from
+ * pcmcia_loop_tuple() to pccard_loop_tuple(). Data is transferred
+ * by a struct pcmcia_cfg_mem.
+ */
+static int pcmcia_do_loop_tuple(tuple_t *tuple, cisparse_t *parse, void *priv)
+{
+ struct pcmcia_loop_mem *loop = priv;
+
+ return loop->loop_tuple(loop->p_dev, tuple, loop->priv_data);
+};
+
+/**
+ * pcmcia_loop_tuple() - loop over tuples in the CIS
+ * @p_dev: the struct pcmcia_device which we need to loop for.
+ * @code: which CIS code shall we look for?
+ * @priv_data: private data to be passed to the loop_tuple function.
+ * @loop_tuple: function to call for each CIS entry of type @function. IT
+ * gets passed the raw tuple and @priv_data.
+ *
+ * pcmcia_loop_tuple() loops over all CIS entries of type @function, and
+ * calls the @loop_tuple function for each entry. If the call to @loop_tuple
+ * returns 0, the loop exits. Returns 0 on success or errorcode otherwise.
+ */
+int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code,
+ int (*loop_tuple) (struct pcmcia_device *p_dev,
+ tuple_t *tuple,
+ void *priv_data),
+ void *priv_data)
+{
+ struct pcmcia_loop_mem loop = {
+ .p_dev = p_dev,
+ .loop_tuple = loop_tuple,
+ .priv_data = priv_data};
+
+ return pccard_loop_tuple(p_dev->socket, p_dev->func, code, NULL,
+ &loop, pcmcia_do_loop_tuple);
+}
+EXPORT_SYMBOL(pcmcia_loop_tuple);
+/* Source: drivers/pcmcia/pcmcia_resource.c */
+
+#endif /* CONFIG_PCMCIA */
+
+#endif /* CONFIG_PCCARD */
+
diff --git a/compat/compat-2.6.35.c b/compat/compat-2.6.35.c
new file mode 100644
index 0000000..7a31a54
--- /dev/null
+++ b/compat/compat-2.6.35.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2010 Kshitij Kulshreshtha <kkhere.geo@gmail.com>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.35.
+ */
+
+#include <linux/compat.h>
+#include <linux/ctype.h>
+
+/**
+ * hex_to_bin - convert a hex digit to its real value
+ * @ch: ascii character represents hex digit
+ *
+ * hex_to_bin() converts one hex digit to its actual value or -1 in case of bad
+ * input.
+ */
+int compat_hex_to_bin(char ch)
+{
+ if ((ch >= '0') && (ch <= '9'))
+ return ch - '0';
+ ch = tolower(ch);
+ if ((ch >= 'a') && (ch <= 'f'))
+ return ch - 'a' + 10;
+ return -1;
+}
+EXPORT_SYMBOL(compat_hex_to_bin);
+
+/**
+ * noop_llseek - No Operation Performed llseek implementation
+ * @file: file structure to seek on
+ * @offset: file offset to seek to
+ * @origin: type of seek
+ *
+ * This is an implementation of ->llseek useable for the rare special case when
+ * userspace expects the seek to succeed but the (device) file is actually not
+ * able to perform the seek. In this case you use noop_llseek() instead of
+ * falling back to the default implementation of ->llseek.
+ */
+loff_t noop_llseek(struct file *file, loff_t offset, int origin)
+{
+ return file->f_pos;
+}
+EXPORT_SYMBOL(noop_llseek);
+
diff --git a/compat/compat-2.6.36.c b/compat/compat-2.6.36.c
new file mode 100644
index 0000000..dfab2ea
--- /dev/null
+++ b/compat/compat-2.6.36.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2010 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.36.
+ */
+
+#include <linux/compat.h>
+#include <linux/usb.h>
+
+#ifdef CONFIG_COMPAT_USB_URB_THREAD_FIX
+/* Callers must hold anchor->lock */
+static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor)
+{
+ urb->anchor = NULL;
+ list_del(&urb->anchor_list);
+ usb_put_urb(urb);
+ if (list_empty(&anchor->urb_list))
+ wake_up(&anchor->wait);
+}
+
+/**
+ * usb_unlink_anchored_urbs - asynchronously cancel transfer requests en masse
+ * @anchor: anchor the requests are bound to
+ *
+ * this allows all outstanding URBs to be unlinked starting
+ * from the back of the queue. This function is asynchronous.
+ * The unlinking is just tiggered. It may happen after this
+ * function has returned.
+ *
+ * This routine should not be called by a driver after its disconnect
+ * method has returned.
+ */
+void compat_usb_unlink_anchored_urbs(struct usb_anchor *anchor)
+{
+ struct urb *victim;
+
+ while ((victim = usb_get_from_anchor(anchor)) != NULL) {
+ usb_unlink_urb(victim);
+ usb_put_urb(victim);
+ }
+}
+EXPORT_SYMBOL_GPL(compat_usb_unlink_anchored_urbs);
+
+/**
+ * usb_get_from_anchor - get an anchor's oldest urb
+ * @anchor: the anchor whose urb you want
+ *
+ * this will take the oldest urb from an anchor,
+ * unanchor and return it
+ */
+struct urb *compat_usb_get_from_anchor(struct usb_anchor *anchor)
+{
+ struct urb *victim;
+ unsigned long flags;
+
+ spin_lock_irqsave(&anchor->lock, flags);
+ if (!list_empty(&anchor->urb_list)) {
+ victim = list_entry(anchor->urb_list.next, struct urb,
+ anchor_list);
+ usb_get_urb(victim);
+ __usb_unanchor_urb(victim, anchor);
+ } else {
+ victim = NULL;
+ }
+ spin_unlock_irqrestore(&anchor->lock, flags);
+
+ return victim;
+}
+EXPORT_SYMBOL_GPL(compat_usb_get_from_anchor);
+
+/**
+ * usb_scuttle_anchored_urbs - unanchor all an anchor's urbs
+ * @anchor: the anchor whose urbs you want to unanchor
+ *
+ * use this to get rid of all an anchor's urbs
+ */
+void compat_usb_scuttle_anchored_urbs(struct usb_anchor *anchor)
+{
+ struct urb *victim;
+ unsigned long flags;
+
+ spin_lock_irqsave(&anchor->lock, flags);
+ while (!list_empty(&anchor->urb_list)) {
+ victim = list_entry(anchor->urb_list.prev, struct urb,
+ anchor_list);
+ __usb_unanchor_urb(victim, anchor);
+ }
+ spin_unlock_irqrestore(&anchor->lock, flags);
+}
+EXPORT_SYMBOL_GPL(compat_usb_scuttle_anchored_urbs);
+
+#endif /* CONFIG_COMPAT_USB_URB_THREAD_FIX */
+
+struct workqueue_struct *system_nrt_wq __read_mostly;
+EXPORT_SYMBOL_GPL(system_nrt_wq);
+
+void compat_system_workqueue_create()
+{
+ system_nrt_wq = create_singlethread_workqueue("events_nrt");
+ WARN_ON(!system_nrt_wq);
+}
+
+void compat_system_workqueue_destroy()
+{
+ destroy_workqueue(system_nrt_wq);
+}
diff --git a/compat/compat-2.6.37.c b/compat/compat-2.6.37.c
new file mode 100644
index 0000000..d7259bb
--- /dev/null
+++ b/compat/compat-2.6.37.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2010 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.37.
+ */
+
+#include <linux/compat.h>
+#include <linux/netdevice.h>
+#include <net/sock.h>
+#include <linux/nsproxy.h>
+#include <linux/vmalloc.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
+static const void *net_current_ns(void)
+{
+ return current->nsproxy->net_ns;
+}
+
+static const void *net_initial_ns(void)
+{
+ return &init_net;
+}
+
+static const void *net_netlink_ns(struct sock *sk)
+{
+ return sock_net(sk);
+}
+
+struct kobj_ns_type_operations net_ns_type_operations = {
+ .type = KOBJ_NS_TYPE_NET,
+ .current_ns = net_current_ns,
+ .netlink_ns = net_netlink_ns,
+ .initial_ns = net_initial_ns,
+};
+EXPORT_SYMBOL_GPL(net_ns_type_operations);
+
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)*/
+
+#undef genl_info
+#undef genl_unregister_family
+
+static LIST_HEAD(compat_nl_fam);
+
+static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family)
+{
+ struct genl_ops *ops;
+
+ list_for_each_entry(ops, &family->family.ops_list, ops.ops_list)
+ if (ops->cmd == cmd)
+ return ops;
+
+ return NULL;
+}
+
+
+static int nl_doit_wrapper(struct sk_buff *skb, struct genl_info *info)
+{
+ struct compat_genl_info compat_info;
+ struct genl_family *family;
+ struct genl_ops *ops;
+ int err;
+
+ list_for_each_entry(family, &compat_nl_fam, list) {
+ if (family->id == info->nlhdr->nlmsg_type)
+ goto found;
+ }
+ return -ENOENT;
+
+found:
+ ops = genl_get_cmd(info->genlhdr->cmd, family);
+ if (!ops)
+ return -ENOENT;
+
+ memset(&compat_info.user_ptr, 0, sizeof(compat_info.user_ptr));
+ compat_info.info = info;
+#define __copy(_field) compat_info._field = info->_field
+ __copy(snd_seq);
+ __copy(snd_pid);
+ __copy(genlhdr);
+ __copy(attrs);
+#undef __copy
+ if (family->pre_doit) {
+ err = family->pre_doit(ops, skb, &compat_info);
+ if (err)
+ return err;
+ }
+
+ err = ops->doit(skb, &compat_info);
+
+ if (family->post_doit)
+ family->post_doit(ops, skb, &compat_info);
+
+ return err;
+}
+
+int compat_genl_register_family_with_ops(struct genl_family *family,
+ struct genl_ops *ops, size_t n_ops)
+{
+ int i, ret;
+
+#define __copy(_field) family->family._field = family->_field
+ __copy(id);
+ __copy(hdrsize);
+ __copy(version);
+ __copy(maxattr);
+ strncpy(family->family.name, family->name, sizeof(family->family.name));
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
+ __copy(netnsok);
+#endif
+#undef __copy
+
+ ret = genl_register_family(&family->family);
+ if (ret < 0)
+ return ret;
+
+ family->attrbuf = family->family.attrbuf;
+ family->id = family->family.id;
+
+ for (i = 0; i < n_ops; i++) {
+#define __copy(_field) ops[i].ops._field = ops[i]._field
+ __copy(cmd);
+ __copy(flags);
+ __copy(policy);
+ __copy(dumpit);
+ __copy(done);
+#undef __copy
+ if (ops[i].doit)
+ ops[i].ops.doit = nl_doit_wrapper;
+ ret = genl_register_ops(&family->family, &ops[i].ops);
+ if (ret < 0)
+ goto error_ops;
+ }
+ list_add(&family->list, &compat_nl_fam);
+
+ return ret;
+
+error_ops:
+ compat_genl_unregister_family(family);
+ return ret;
+}
+EXPORT_SYMBOL(compat_genl_register_family_with_ops);
+
+int compat_genl_unregister_family(struct genl_family *family)
+{
+ int err;
+ err = genl_unregister_family(&family->family);
+ list_del(&family->list);
+ return err;
+}
+EXPORT_SYMBOL(compat_genl_unregister_family);
+
+#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+
+#undef led_brightness_set
+#undef led_classdev_unregister
+
+static DEFINE_SPINLOCK(led_lock);
+static LIST_HEAD(led_timers);
+
+struct led_timer {
+ struct list_head list;
+ struct led_classdev *cdev;
+ struct timer_list blink_timer;
+ unsigned long blink_delay_on;
+ unsigned long blink_delay_off;
+ int blink_brightness;
+};
+
+static void led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ led_cdev->brightness = brightness;
+ led_cdev->brightness_set(led_cdev, brightness);
+}
+
+static struct led_timer *led_get_timer(struct led_classdev *led_cdev)
+{
+ struct led_timer *p;
+ unsigned long flags;
+
+ spin_lock_irqsave(&led_lock, flags);
+ list_for_each_entry(p, &led_timers, list) {
+ if (p->cdev == led_cdev)
+ goto found;
+ }
+ p = NULL;
+found:
+ spin_unlock_irqrestore(&led_lock, flags);
+ return p;
+}
+
+static void led_stop_software_blink(struct led_timer *led)
+{
+ del_timer_sync(&led->blink_timer);
+ led->blink_delay_on = 0;
+ led->blink_delay_off = 0;
+}
+
+static void led_timer_function(unsigned long data)
+{
+ struct led_timer *led = (struct led_timer *)data;
+ unsigned long brightness;
+ unsigned long delay;
+
+ if (!led->blink_delay_on || !led->blink_delay_off) {
+ led->cdev->brightness_set(led->cdev, LED_OFF);
+ return;
+ }
+
+ brightness = led->cdev->brightness;
+ if (!brightness) {
+ /* Time to switch the LED on. */
+ brightness = led->blink_brightness;
+ delay = led->blink_delay_on;
+ } else {
+ /* Store the current brightness value to be able
+ * to restore it when the delay_off period is over.
+ */
+ led->blink_brightness = brightness;
+ brightness = LED_OFF;
+ delay = led->blink_delay_off;
+ }
+
+ led_brightness_set(led->cdev, brightness);
+ mod_timer(&led->blink_timer, jiffies + msecs_to_jiffies(delay));
+}
+
+static struct led_timer *led_new_timer(struct led_classdev *led_cdev)
+{
+ struct led_timer *led;
+ unsigned long flags;
+
+ led = kzalloc(sizeof(struct led_timer), GFP_ATOMIC);
+ if (!led)
+ return NULL;
+
+ led->cdev = led_cdev;
+ init_timer(&led->blink_timer);
+ led->blink_timer.function = led_timer_function;
+ led->blink_timer.data = (unsigned long) led;
+
+ spin_lock_irqsave(&led_lock, flags);
+ list_add(&led->list, &led_timers);
+ spin_unlock_irqrestore(&led_lock, flags);
+
+ return led;
+}
+
+void led_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct led_timer *led;
+ int current_brightness;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25))
+ if (led_cdev->blink_set &&
+ !led_cdev->blink_set(led_cdev, delay_on, delay_off))
+ return;
+#endif
+
+ led = led_get_timer(led_cdev);
+ if (!led) {
+ led = led_new_timer(led_cdev);
+ if (!led)
+ return;
+ }
+
+ /* blink with 1 Hz as default if nothing specified */
+ if (!*delay_on && !*delay_off)
+ *delay_on = *delay_off = 500;
+
+ if (led->blink_delay_on == *delay_on &&
+ led->blink_delay_off == *delay_off)
+ return;
+
+ current_brightness = led_cdev->brightness;
+ if (current_brightness)
+ led->blink_brightness = current_brightness;
+ if (!led->blink_brightness)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
+ led->blink_brightness = led_cdev->max_brightness;
+#else
+ led->blink_brightness = LED_FULL;
+#endif
+
+ led_stop_software_blink(led);
+ led->blink_delay_on = *delay_on;
+ led->blink_delay_off = *delay_off;
+
+ /* never on - don't blink */
+ if (!*delay_on)
+ return;
+
+ /* never off - just set to brightness */
+ if (!*delay_off) {
+ led_brightness_set(led_cdev, led->blink_brightness);
+ return;
+ }
+
+ mod_timer(&led->blink_timer, jiffies + 1);
+}
+EXPORT_SYMBOL(led_blink_set);
+
+void compat_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct led_timer *led = led_get_timer(led_cdev);
+
+ if (led)
+ led_stop_software_blink(led);
+
+ return led_cdev->brightness_set(led_cdev, brightness);
+}
+EXPORT_SYMBOL(compat_led_brightness_set);
+
+void compat_led_classdev_unregister(struct led_classdev *led_cdev)
+{
+ struct led_timer *led = led_get_timer(led_cdev);
+ unsigned long flags;
+
+ if (led) {
+ del_timer_sync(&led->blink_timer);
+ spin_lock_irqsave(&led_lock, flags);
+ list_del(&led->list);
+ spin_unlock_irqrestore(&led_lock, flags);
+ kfree(led);
+ }
+
+ led_classdev_unregister(led_cdev);
+}
+EXPORT_SYMBOL(compat_led_classdev_unregister);
+
+/**
+ * vzalloc - allocate virtually contiguous memory with zero fill
+ * @size: allocation size
+ * Allocate enough pages to cover @size from the page level
+ * allocator and map them into contiguous kernel virtual space.
+ * The memory allocated is set to zero.
+ *
+ * For tight control over page level allocator and protection flags
+ * use __vmalloc() instead.
+ */
+void *compat_vzalloc(unsigned long size)
+{
+ void *buf;
+ buf = vmalloc(size);
+ if (buf)
+ memset(buf, 0, size);
+ return buf;
+}
+EXPORT_SYMBOL(compat_vzalloc);
+
+#endif
diff --git a/compat/compat-2.6.38.c b/compat/compat-2.6.38.c
new file mode 100644
index 0000000..172aa19
--- /dev/null
+++ b/compat/compat-2.6.38.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2010 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.38.
+ */
+
+#include <linux/compat.h>
+#include <linux/module.h>
+#include <linux/bug.h>
+
+/**
+ * ewma_init() - Initialize EWMA parameters
+ * @avg: Average structure
+ * @factor: Factor to use for the scaled up internal value. The maximum value
+ * of averages can be ULONG_MAX/(factor*weight).
+ * @weight: Exponential weight, or decay rate. This defines how fast the
+ * influence of older values decreases. Has to be bigger than 1.
+ *
+ * Initialize the EWMA parameters for a given struct ewma @avg.
+ */
+void ewma_init(struct ewma *avg, unsigned long factor, unsigned long weight)
+{
+ WARN_ON(weight <= 1 || factor == 0);
+ avg->internal = 0;
+ avg->weight = weight;
+ avg->factor = factor;
+}
+EXPORT_SYMBOL(ewma_init);
+
+/**
+ * ewma_add() - Exponentially weighted moving average (EWMA)
+ * @avg: Average structure
+ * @val: Current value
+ *
+ * Add a sample to the average.
+ */
+struct ewma *ewma_add(struct ewma *avg, unsigned long val)
+{
+ avg->internal = avg->internal ?
+ (((avg->internal * (avg->weight - 1)) +
+ (val * avg->factor)) / avg->weight) :
+ (val * avg->factor);
+ return avg;
+}
+EXPORT_SYMBOL(ewma_add);
+
diff --git a/compat/compat-2.6.39.c b/compat/compat-2.6.39.c
new file mode 100644
index 0000000..5bb9322
--- /dev/null
+++ b/compat/compat-2.6.39.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2011 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.39.
+ */
+
+#include <linux/compat.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
+/*
+ * Termios Helper Methods
+ */
+static void unset_locked_termios(struct ktermios *termios,
+ struct ktermios *old,
+ struct ktermios *locked)
+{
+ int i;
+
+#define NOSET_MASK(x, y, z) (x = ((x) & ~(z)) | ((y) & (z)))
+
+ if (!locked) {
+ printk(KERN_WARNING "Warning?!? termios_locked is NULL.\n");
+ return;
+ }
+
+ NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag);
+ NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag);
+ NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag);
+ NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag);
+ termios->c_line = locked->c_line ? old->c_line : termios->c_line;
+ for (i = 0; i < NCCS; i++)
+ termios->c_cc[i] = locked->c_cc[i] ?
+ old->c_cc[i] : termios->c_cc[i];
+ /* FIXME: What should we do for i/ospeed */
+}
+
+/**
+ * tty_set_termios - update termios values
+ * @tty: tty to update
+ * @new_termios: desired new value
+ *
+ * Perform updates to the termios values set on this terminal. There
+ * is a bit of layering violation here with n_tty in terms of the
+ * internal knowledge of this function.
+ *
+ * Locking: termios_mutex
+ */
+int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
+{
+ struct ktermios old_termios;
+ struct tty_ldisc *ld;
+ unsigned long flags;
+
+ /*
+ * Perform the actual termios internal changes under lock.
+ */
+
+
+ /* FIXME: we need to decide on some locking/ordering semantics
+ for the set_termios notification eventually */
+ mutex_lock(&tty->termios_mutex);
+ old_termios = *tty->termios;
+ *tty->termios = *new_termios;
+ unset_locked_termios(tty->termios, &old_termios, tty->termios_locked);
+
+ /* See if packet mode change of state. */
+ if (tty->link && tty->link->packet) {
+ int extproc = (old_termios.c_lflag & EXTPROC) |
+ (tty->termios->c_lflag & EXTPROC);
+ int old_flow = ((old_termios.c_iflag & IXON) &&
+ (old_termios.c_cc[VSTOP] == '\023') &&
+ (old_termios.c_cc[VSTART] == '\021'));
+ int new_flow = (I_IXON(tty) &&
+ STOP_CHAR(tty) == '\023' &&
+ START_CHAR(tty) == '\021');
+ if ((old_flow != new_flow) || extproc) {
+ spin_lock_irqsave(&tty->ctrl_lock, flags);
+ if (old_flow != new_flow) {
+ tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
+ if (new_flow)
+ tty->ctrl_status |= TIOCPKT_DOSTOP;
+ else
+ tty->ctrl_status |= TIOCPKT_NOSTOP;
+ }
+ if (extproc)
+ tty->ctrl_status |= TIOCPKT_IOCTL;
+ spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+ wake_up_interruptible(&tty->link->read_wait);
+ }
+ }
+
+ if (tty->ops->set_termios)
+ (*tty->ops->set_termios)(tty, &old_termios);
+ else
+ tty_termios_copy_hw(tty->termios, &old_termios);
+
+ ld = tty_ldisc_ref(tty);
+ if (ld != NULL) {
+ if (ld->ops->set_termios)
+ (ld->ops->set_termios)(tty, &old_termios);
+ tty_ldisc_deref(ld);
+ }
+ mutex_unlock(&tty->termios_mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tty_set_termios);
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) */
+
diff --git a/compat/compat-3.0.c b/compat/compat-3.0.c
new file mode 100644
index 0000000..f517e9f
--- /dev/null
+++ b/compat/compat-3.0.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2011 Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright 2011 Alexey Dobriyan <adobriyan@gmail.com>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 3.0.
+ */
+
+#include <linux/compat.h>
+#include <linux/if_ether.h>
+
+int mac_pton(const char *s, u8 *mac)
+{
+ int i;
+
+ /* XX:XX:XX:XX:XX:XX */
+ if (strlen(s) < 3 * ETH_ALEN - 1)
+ return 0;
+
+ /* Don't dirty result unless string is valid MAC. */
+ for (i = 0; i < ETH_ALEN; i++) {
+ if (!strchr("0123456789abcdefABCDEF", s[i * 3]))
+ return 0;
+ if (!strchr("0123456789abcdefABCDEF", s[i * 3 + 1]))
+ return 0;
+ if (i != ETH_ALEN - 1 && s[i * 3 + 2] != ':')
+ return 0;
+ }
+ for (i = 0; i < ETH_ALEN; i++) {
+ mac[i] = (hex_to_bin(s[i * 3]) << 4) | hex_to_bin(s[i * 3 + 1]);
+ }
+ return 1;
+}
+EXPORT_SYMBOL(mac_pton);
+
+#define kstrto_from_user(f, g, type) \
+int f(const char __user *s, size_t count, unsigned int base, type *res) \
+{ \
+ /* sign, base 2 representation, newline, terminator */ \
+ char buf[1 + sizeof(type) * 8 + 1 + 1]; \
+ \
+ count = min(count, sizeof(buf) - 1); \
+ if (copy_from_user(buf, s, count)) \
+ return -EFAULT; \
+ buf[count] = '\0'; \
+ return g(buf, base, res); \
+} \
+EXPORT_SYMBOL(f)
+
+kstrto_from_user(kstrtoull_from_user, kstrtoull, unsigned long long);
+kstrto_from_user(kstrtoll_from_user, kstrtoll, long long);
+kstrto_from_user(kstrtoul_from_user, kstrtoul, unsigned long);
+kstrto_from_user(kstrtol_from_user, kstrtol, long);
+kstrto_from_user(kstrtouint_from_user, kstrtouint, unsigned int);
+kstrto_from_user(kstrtoint_from_user, kstrtoint, int);
+kstrto_from_user(kstrtou16_from_user, kstrtou16, u16);
+kstrto_from_user(kstrtos16_from_user, kstrtos16, s16);
+kstrto_from_user(kstrtou8_from_user, kstrtou8, u8);
+kstrto_from_user(kstrtos8_from_user, kstrtos8, s8);
diff --git a/compat/compat-3.2.c b/compat/compat-3.2.c
new file mode 100644
index 0000000..ab5bd1f
--- /dev/null
+++ b/compat/compat-3.2.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2012 Luis R. Rodriguez <mcgrof@frijolero.org>
+ *
+ * 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.
+ *
+ * Compatibility file for Linux wireless for kernels 3.2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/device.h>
+
+int __netdev_printk(const char *level, const struct net_device *dev,
+ struct va_format *vaf)
+{
+ int r;
+
+ if (dev && dev->dev.parent)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35))
+ r = dev_printk(level, dev->dev.parent, "%s: %pV",
+ netdev_name(dev), vaf);
+#else
+ /* XXX: this could likely be done better but I'm lazy */
+ r = printk("%s%s: %pV", level, netdev_name(dev), vaf);
+#endif
+ else if (dev)
+ r = printk("%s%s: %pV", level, netdev_name(dev), vaf);
+ else
+ r = printk("%s(NULL net_device): %pV", level, vaf);
+
+ return r;
+}
+EXPORT_SYMBOL(__netdev_printk);
diff --git a/compat/compat_atomic.c b/compat/compat_atomic.c
new file mode 100644
index 0000000..747e275
--- /dev/null
+++ b/compat/compat_atomic.c
@@ -0,0 +1,33 @@
+#include <linux/spinlock.h>
+#include <linux/module.h>
+
+#if !((LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) && (defined(CONFIG_UML) || defined(CONFIG_X86))) && !((LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)) && defined(CONFIG_ARM) && !defined(CONFIG_GENERIC_ATOMIC64))
+
+static DEFINE_SPINLOCK(lock);
+
+long long atomic64_read(const atomic64_t *v)
+{
+ unsigned long flags;
+ long long val;
+
+ spin_lock_irqsave(&lock, flags);
+ val = v->counter;
+ spin_unlock_irqrestore(&lock, flags);
+ return val;
+}
+EXPORT_SYMBOL(atomic64_read);
+
+long long atomic64_add_return(long long a, atomic64_t *v)
+{
+ unsigned long flags;
+ long long val;
+
+ spin_lock_irqsave(&lock, flags);
+ val = v->counter += a;
+ spin_unlock_irqrestore(&lock, flags);
+ return val;
+}
+EXPORT_SYMBOL(atomic64_add_return);
+
+#endif
+
diff --git a/compat/compat_firmware_class.c b/compat/compat_firmware_class.c
new file mode 100644
index 0000000..4eb5ba7
--- /dev/null
+++ b/compat/compat_firmware_class.c
@@ -0,0 +1,759 @@
+/*
+ * firmware_class.c - Multi purpose firmware loading support
+ *
+ * Copyright (c) 2003 Manuel Estrada Sainz
+ *
+ * Please see Documentation/firmware_class/ for more information.
+ *
+ */
+
+#include <linux/capability.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <linux/kthread.h>
+#include <linux/highmem.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+
+#define to_dev(obj) container_of(obj, struct device, kobj)
+
+MODULE_AUTHOR("Manuel Estrada Sainz");
+MODULE_DESCRIPTION("Multi purpose firmware loading support");
+MODULE_LICENSE("GPL");
+
+/* Builtin firmware support */
+
+//#ifdef CONFIG_FW_LOADER
+#if 0
+
+extern struct builtin_fw __start_builtin_fw[];
+extern struct builtin_fw __end_builtin_fw[];
+
+static bool fw_get_builtin_firmware(struct firmware *fw, const char *name)
+{
+ struct builtin_fw *b_fw;
+
+ for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) {
+ if (strcmp(name, b_fw->name) == 0) {
+ fw->size = b_fw->size;
+ fw->data = b_fw->data;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool fw_is_builtin_firmware(const struct firmware *fw)
+{
+ struct builtin_fw *b_fw;
+
+ for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++)
+ if (fw->data == b_fw->data)
+ return true;
+
+ return false;
+}
+
+#else /* Module case - no builtin firmware support */
+
+static inline bool fw_get_builtin_firmware(struct firmware *fw, const char *name)
+{
+ return false;
+}
+
+static inline bool fw_is_builtin_firmware(const struct firmware *fw)
+{
+ return false;
+}
+#endif
+
+enum {
+ FW_STATUS_LOADING,
+ FW_STATUS_DONE,
+ FW_STATUS_ABORT,
+};
+
+static int loading_timeout = 60; /* In seconds */
+
+/* fw_lock could be moved to 'struct firmware_priv' but since it is just
+ * guarding for corner cases a global lock should be OK */
+static DEFINE_MUTEX(fw_lock);
+
+struct firmware_priv {
+ struct completion completion;
+ struct firmware *fw;
+ unsigned long status;
+ struct page **pages;
+ int nr_pages;
+ int page_array_size;
+ struct timer_list timeout;
+ struct device dev;
+ bool nowait;
+ char fw_id[];
+};
+
+static struct firmware_priv *to_firmware_priv(struct device *dev)
+{
+ return container_of(dev, struct firmware_priv, dev);
+}
+
+static void fw_load_abort(struct firmware_priv *fw_priv)
+{
+ set_bit(FW_STATUS_ABORT, &fw_priv->status);
+ wmb();
+ complete(&fw_priv->completion);
+}
+
+static ssize_t firmware_timeout_show(struct class *class,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", loading_timeout);
+}
+
+/**
+ * firmware_timeout_store - set number of seconds to wait for firmware
+ * @class: device class pointer
+ * @buf: buffer to scan for timeout value
+ * @count: number of bytes in @buf
+ *
+ * Sets the number of seconds to wait for the firmware. Once
+ * this expires an error will be returned to the driver and no
+ * firmware will be provided.
+ *
+ * Note: zero means 'wait forever'.
+ **/
+static ssize_t firmware_timeout_store(struct class *class,
+ const char *buf, size_t count)
+{
+ loading_timeout = simple_strtol(buf, NULL, 10);
+ if (loading_timeout < 0)
+ loading_timeout = 0;
+
+ return count;
+}
+
+static struct class_attribute firmware_class_attrs[] = {
+ __ATTR(timeout, S_IWUSR | S_IRUGO,
+ firmware_timeout_show, firmware_timeout_store),
+ __ATTR_NULL
+};
+
+static void fw_dev_release(struct device *dev)
+{
+ struct firmware_priv *fw_priv = to_firmware_priv(dev);
+ int i;
+
+ for (i = 0; i < fw_priv->nr_pages; i++)
+ __free_page(fw_priv->pages[i]);
+ kfree(fw_priv->pages);
+ kfree(fw_priv);
+
+ module_put(THIS_MODULE);
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24))
+static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct firmware_priv *fw_priv = to_firmware_priv(dev);
+
+ if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->fw_id))
+ return -ENOMEM;
+ if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout))
+ return -ENOMEM;
+ if (add_uevent_var(env, "ASYNC=%d", fw_priv->nowait))
+ return -ENOMEM;
+
+ return 0;
+}
+#else
+static int firmware_uevent(struct device *dev, char **envp,
+ int num_envp, char *buf, int size)
+{
+ struct firmware_priv *fw_priv = to_firmware_priv(dev);
+ int error, len = 0, i = 0;
+
+ error = add_uevent_var(envp, num_envp, &i,
+ buf, size, &len,
+ "FIRMWARE=%s", fw_priv->fw_id);
+ if (error)
+ goto exit;
+
+ error = add_uevent_var(envp, num_envp, &i,
+ buf, size, &len,
+ "TIMEOUT=%i", loading_timeout);
+ if (error)
+ goto exit;
+ error = add_uevent_var(envp, num_envp, &i,
+ buf, size, &len,
+ "ASYNC=%i", fw_priv->nowait);
+ if (error)
+ goto exit;
+
+ return 0;
+exit:
+ envp[i] = NULL;
+ return error;
+}
+#endif
+
+static struct class firmware_class = {
+ .name = "compat_firmware",
+ .class_attrs = firmware_class_attrs,
+ .dev_uevent = firmware_uevent,
+ .dev_release = fw_dev_release,
+};
+
+static ssize_t firmware_loading_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct firmware_priv *fw_priv = to_firmware_priv(dev);
+ int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status);
+
+ return sprintf(buf, "%d\n", loading);
+}
+
+static void firmware_free_data(const struct firmware *fw)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ int i;
+ vunmap(fw->data);
+ if (fw->pages) {
+ for (i = 0; i < PFN_UP(fw->size); i++)
+ __free_page(fw->pages[i]);
+ kfree(fw->pages);
+ }
+#else
+ vunmap(fw->data);
+#endif
+}
+
+/* Some architectures don't have PAGE_KERNEL_RO */
+#ifndef PAGE_KERNEL_RO
+#define PAGE_KERNEL_RO PAGE_KERNEL
+#endif
+/**
+ * firmware_loading_store - set value in the 'loading' control file
+ * @dev: device pointer
+ * @buf: buffer to scan for loading control value
+ * @count: number of bytes in @buf
+ *
+ * The relevant values are:
+ *
+ * 1: Start a load, discarding any previous partial load.
+ * 0: Conclude the load and hand the data to the driver code.
+ * -1: Conclude the load with an error and discard any written data.
+ **/
+static ssize_t firmware_loading_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct firmware_priv *fw_priv = to_firmware_priv(dev);
+ int loading = simple_strtol(buf, NULL, 10);
+ int i;
+
+ switch (loading) {
+ case 1:
+ mutex_lock(&fw_lock);
+ if (!fw_priv->fw) {
+ mutex_unlock(&fw_lock);
+ break;
+ }
+ firmware_free_data(fw_priv->fw);
+ memset(fw_priv->fw, 0, sizeof(struct firmware));
+ /* If the pages are not owned by 'struct firmware' */
+ for (i = 0; i < fw_priv->nr_pages; i++)
+ __free_page(fw_priv->pages[i]);
+ kfree(fw_priv->pages);
+ fw_priv->pages = NULL;
+ fw_priv->page_array_size = 0;
+ fw_priv->nr_pages = 0;
+ set_bit(FW_STATUS_LOADING, &fw_priv->status);
+ mutex_unlock(&fw_lock);
+ break;
+ case 0:
+ if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
+ vunmap(fw_priv->fw->data);
+ fw_priv->fw->data = vmap(fw_priv->pages,
+ fw_priv->nr_pages,
+ 0, PAGE_KERNEL_RO);
+ if (!fw_priv->fw->data) {
+ dev_err(dev, "%s: vmap() failed\n", __func__);
+ goto err;
+ }
+ /* Pages are now owned by 'struct firmware' */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ fw_priv->fw->pages = fw_priv->pages;
+ fw_priv->pages = NULL;
+#endif
+
+ fw_priv->page_array_size = 0;
+ fw_priv->nr_pages = 0;
+ complete(&fw_priv->completion);
+ clear_bit(FW_STATUS_LOADING, &fw_priv->status);
+ break;
+ }
+ /* fallthrough */
+ default:
+ dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
+ /* fallthrough */
+ case -1:
+ err:
+ fw_load_abort(fw_priv);
+ break;
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
+
+#if defined(CONFIG_COMPAT_FIRMWARE_DATA_RW_NEEDS_FILP)
+static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buffer, loff_t offset, size_t count)
+#else
+static ssize_t firmware_data_read(struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buffer, loff_t offset, size_t count)
+#endif
+{
+ struct device *dev = to_dev(kobj);
+ struct firmware_priv *fw_priv = to_firmware_priv(dev);
+ struct firmware *fw;
+ ssize_t ret_count;
+
+ mutex_lock(&fw_lock);
+ fw = fw_priv->fw;
+ if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
+ ret_count = -ENODEV;
+ goto out;
+ }
+ if (offset > fw->size) {
+ ret_count = 0;
+ goto out;
+ }
+ if (count > fw->size - offset)
+ count = fw->size - offset;
+
+ ret_count = count;
+
+ while (count) {
+ void *page_data;
+ int page_nr = offset >> PAGE_SHIFT;
+ int page_ofs = offset & (PAGE_SIZE-1);
+ int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
+
+ page_data = kmap(fw_priv->pages[page_nr]);
+
+ memcpy(buffer, page_data + page_ofs, page_cnt);
+
+ kunmap(fw_priv->pages[page_nr]);
+ buffer += page_cnt;
+ offset += page_cnt;
+ count -= page_cnt;
+ }
+out:
+ mutex_unlock(&fw_lock);
+ return ret_count;
+}
+
+static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
+{
+ int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT;
+
+ /* If the array of pages is too small, grow it... */
+ if (fw_priv->page_array_size < pages_needed) {
+ int new_array_size = max(pages_needed,
+ fw_priv->page_array_size * 2);
+ struct page **new_pages;
+
+ new_pages = kmalloc(new_array_size * sizeof(void *),
+ GFP_KERNEL);
+ if (!new_pages) {
+ fw_load_abort(fw_priv);
+ return -ENOMEM;
+ }
+ memcpy(new_pages, fw_priv->pages,
+ fw_priv->page_array_size * sizeof(void *));
+ memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
+ (new_array_size - fw_priv->page_array_size));
+ kfree(fw_priv->pages);
+ fw_priv->pages = new_pages;
+ fw_priv->page_array_size = new_array_size;
+ }
+
+ while (fw_priv->nr_pages < pages_needed) {
+ fw_priv->pages[fw_priv->nr_pages] =
+ alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
+
+ if (!fw_priv->pages[fw_priv->nr_pages]) {
+ fw_load_abort(fw_priv);
+ return -ENOMEM;
+ }
+ fw_priv->nr_pages++;
+ }
+ return 0;
+}
+
+/**
+ * firmware_data_write - write method for firmware
+ * @kobj: kobject for the device
+ * @bin_attr: bin_attr structure
+ * @buffer: buffer being written
+ * @offset: buffer offset for write in total data store area
+ * @count: buffer size
+ *
+ * Data written to the 'data' attribute will be later handed to
+ * the driver as a firmware image.
+ **/
+#if defined(CONFIG_COMPAT_FIRMWARE_DATA_RW_NEEDS_FILP)
+static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buffer, loff_t offset, size_t count)
+#else
+static ssize_t firmware_data_write(struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buffer, loff_t offset, size_t count)
+#endif
+{
+ struct device *dev = to_dev(kobj);
+ struct firmware_priv *fw_priv = to_firmware_priv(dev);
+ struct firmware *fw;
+ ssize_t retval;
+
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ mutex_lock(&fw_lock);
+ fw = fw_priv->fw;
+ if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
+ retval = -ENODEV;
+ goto out;
+ }
+ retval = fw_realloc_buffer(fw_priv, offset + count);
+ if (retval)
+ goto out;
+
+ retval = count;
+
+ while (count) {
+ void *page_data;
+ int page_nr = offset >> PAGE_SHIFT;
+ int page_ofs = offset & (PAGE_SIZE - 1);
+ int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
+
+ page_data = kmap(fw_priv->pages[page_nr]);
+
+ memcpy(page_data + page_ofs, buffer, page_cnt);
+
+ kunmap(fw_priv->pages[page_nr]);
+ buffer += page_cnt;
+ offset += page_cnt;
+ count -= page_cnt;
+ }
+
+ fw->size = max_t(size_t, offset, fw->size);
+out:
+ mutex_unlock(&fw_lock);
+ return retval;
+}
+
+static struct bin_attribute firmware_attr_data = {
+ .attr = { .name = "data", .mode = 0644 },
+ .size = 0,
+ .read = firmware_data_read,
+ .write = firmware_data_write,
+};
+
+static void firmware_class_timeout(u_long data)
+{
+ struct firmware_priv *fw_priv = (struct firmware_priv *) data;
+
+ fw_load_abort(fw_priv);
+}
+
+static struct firmware_priv *
+fw_create_instance(struct firmware *firmware, const char *fw_name,
+ struct device *device, bool uevent, bool nowait)
+{
+ struct firmware_priv *fw_priv;
+ struct device *f_dev;
+ int error;
+
+ fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL);
+ if (!fw_priv) {
+ dev_err(device, "%s: kmalloc failed\n", __func__);
+ error = -ENOMEM;
+ goto err_out;
+ }
+
+ fw_priv->fw = firmware;
+ fw_priv->nowait = nowait;
+ strcpy(fw_priv->fw_id, fw_name);
+ init_completion(&fw_priv->completion);
+ setup_timer(&fw_priv->timeout,
+ firmware_class_timeout, (u_long) fw_priv);
+
+ f_dev = &fw_priv->dev;
+
+ device_initialize(f_dev);
+ dev_set_name(f_dev, "%s", dev_name(device));
+ f_dev->parent = device;
+ f_dev->class = &firmware_class;
+
+ dev_set_uevent_suppress(f_dev, true);
+
+ /* Need to pin this module until class device is destroyed */
+ __module_get(THIS_MODULE);
+
+ error = device_add(f_dev);
+ if (error) {
+ dev_err(device, "%s: device_register failed\n", __func__);
+ goto err_put_dev;
+ }
+
+ error = device_create_bin_file(f_dev, &firmware_attr_data);
+ if (error) {
+ dev_err(device, "%s: sysfs_create_bin_file failed\n", __func__);
+ goto err_del_dev;
+ }
+
+ error = device_create_file(f_dev, &dev_attr_loading);
+ if (error) {
+ dev_err(device, "%s: device_create_file failed\n", __func__);
+ goto err_del_bin_attr;
+ }
+
+ if (uevent)
+ dev_set_uevent_suppress(f_dev, false);
+
+ return fw_priv;
+
+err_del_bin_attr:
+ device_remove_bin_file(f_dev, &firmware_attr_data);
+err_del_dev:
+ device_del(f_dev);
+err_put_dev:
+ put_device(f_dev);
+err_out:
+ return ERR_PTR(error);
+}
+
+static void fw_destroy_instance(struct firmware_priv *fw_priv)
+{
+ struct device *f_dev = &fw_priv->dev;
+
+ device_remove_file(f_dev, &dev_attr_loading);
+ device_remove_bin_file(f_dev, &firmware_attr_data);
+ device_unregister(f_dev);
+}
+
+static int _request_firmware(const struct firmware **firmware_p,
+ const char *name, struct device *device,
+ bool uevent, bool nowait)
+{
+ struct firmware_priv *fw_priv;
+ struct firmware *firmware;
+ int retval = 0;
+
+ if (!firmware_p)
+ return -EINVAL;
+
+ *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
+ if (!firmware) {
+ dev_err(device, "%s: kmalloc(struct firmware) failed\n",
+ __func__);
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ if (fw_get_builtin_firmware(firmware, name)) {
+ dev_dbg(device, "firmware: using built-in firmware %s\n", name);
+ return 0;
+ }
+
+ if (uevent)
+ dev_dbg(device, "firmware: requesting %s\n", name);
+
+ fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
+ if (IS_ERR(fw_priv)) {
+ retval = PTR_ERR(fw_priv);
+ goto out;
+ }
+
+ if (uevent) {
+ if (loading_timeout > 0)
+ mod_timer(&fw_priv->timeout,
+ round_jiffies_up(jiffies +
+ loading_timeout * HZ));
+
+ kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
+ }
+
+ wait_for_completion(&fw_priv->completion);
+
+ set_bit(FW_STATUS_DONE, &fw_priv->status);
+ del_timer_sync(&fw_priv->timeout);
+
+ mutex_lock(&fw_lock);
+ if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status))
+ retval = -ENOENT;
+ fw_priv->fw = NULL;
+ mutex_unlock(&fw_lock);
+
+ fw_destroy_instance(fw_priv);
+
+out:
+ if (retval) {
+ release_firmware(firmware);
+ *firmware_p = NULL;
+ }
+
+ return retval;
+}
+
+/**
+ * request_firmware: - send firmware request and wait for it
+ * @firmware_p: pointer to firmware image
+ * @name: name of firmware file
+ * @device: device for which firmware is being loaded
+ *
+ * @firmware_p will be used to return a firmware image by the name
+ * of @name for device @device.
+ *
+ * Should be called from user context where sleeping is allowed.
+ *
+ * @name will be used as $FIRMWARE in the uevent environment and
+ * should be distinctive enough not to be confused with any other
+ * firmware image for this or any other device.
+ **/
+int
+request_firmware(const struct firmware **firmware_p, const char *name,
+ struct device *device)
+{
+ int uevent = 1;
+ return _request_firmware(firmware_p, name, device, uevent, false);
+}
+
+/**
+ * release_firmware: - release the resource associated with a firmware image
+ * @fw: firmware resource to release
+ **/
+void release_firmware(const struct firmware *fw)
+{
+ if (fw) {
+ if (!fw_is_builtin_firmware(fw))
+ firmware_free_data(fw);
+ kfree(fw);
+ }
+}
+
+/* Async support */
+struct firmware_work {
+ struct work_struct work;
+ struct module *module;
+ const char *name;
+ struct device *device;
+ void *context;
+ void (*cont)(const struct firmware *fw, void *context);
+ int uevent;
+};
+
+static int request_firmware_work_func(void *arg)
+{
+ struct firmware_work *fw_work = arg;
+ const struct firmware *fw;
+ int ret;
+
+ if (!arg) {
+ WARN_ON(1);
+ return 0;
+ }
+
+ ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+ fw_work->uevent, true);
+ fw_work->cont(fw, fw_work->context);
+
+ module_put(fw_work->module);
+ kfree(fw_work);
+
+ return ret;
+}
+
+/**
+ * request_firmware_nowait - asynchronous version of request_firmware
+ * @module: module requesting the firmware
+ * @uevent: sends uevent to copy the firmware image if this flag
+ * is non-zero else the firmware copy must be done manually.
+ * @name: name of firmware file
+ * @device: device for which firmware is being loaded
+ * @gfp: allocation flags
+ * @context: will be passed over to @cont, and
+ * @fw may be %NULL if firmware request fails.
+ * @cont: function will be called asynchronously when the firmware
+ * request is over.
+ *
+ * Asynchronous variant of request_firmware() for user contexts where
+ * it is not possible to sleep for long time. It can't be called
+ * in atomic contexts.
+ **/
+int
+request_firmware_nowait(
+ struct module *module, int uevent,
+ const char *name, struct device *device, gfp_t gfp, void *context,
+ void (*cont)(const struct firmware *fw, void *context))
+{
+ struct task_struct *task;
+ struct firmware_work *fw_work;
+
+ fw_work = kzalloc(sizeof (struct firmware_work), gfp);
+ if (!fw_work)
+ return -ENOMEM;
+
+ fw_work->module = module;
+ fw_work->name = name;
+ fw_work->device = device;
+ fw_work->context = context;
+ fw_work->cont = cont;
+ fw_work->uevent = uevent;
+
+ if (!try_module_get(module)) {
+ kfree(fw_work);
+ return -EFAULT;
+ }
+
+ task = kthread_run(request_firmware_work_func, fw_work,
+ "firmware/%s", name);
+ if (IS_ERR(task)) {
+ fw_work->cont(NULL, fw_work->context);
+ module_put(fw_work->module);
+ kfree(fw_work);
+ return PTR_ERR(task);
+ }
+
+ return 0;
+}
+
+static int __init firmware_class_init(void)
+{
+ return class_register(&firmware_class);
+}
+
+static void __exit firmware_class_exit(void)
+{
+ class_unregister(&firmware_class);
+}
+
+fs_initcall(firmware_class_init);
+module_exit(firmware_class_exit);
+
+EXPORT_SYMBOL(release_firmware);
+EXPORT_SYMBOL(request_firmware);
+EXPORT_SYMBOL(request_firmware_nowait);
diff --git a/compat/cordic.c b/compat/cordic.c
new file mode 100644
index 0000000..aa27a88
--- /dev/null
+++ b/compat/cordic.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2011 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 <linux/module.h>
+#include <linux/cordic.h>
+
+#define CORDIC_ANGLE_GEN 39797
+#define CORDIC_PRECISION_SHIFT 16
+#define CORDIC_NUM_ITER (CORDIC_PRECISION_SHIFT + 2)
+
+#define FIXED(X) ((s32)((X) << CORDIC_PRECISION_SHIFT))
+#define FLOAT(X) (((X) >= 0) \
+ ? ((((X) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1) \
+ : -((((-(X)) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1))
+
+static const s32 arctan_table[] = {
+ 2949120,
+ 1740967,
+ 919879,
+ 466945,
+ 234379,
+ 117304,
+ 58666,
+ 29335,
+ 14668,
+ 7334,
+ 3667,
+ 1833,
+ 917,
+ 458,
+ 229,
+ 115,
+ 57,
+ 29
+};
+
+/*
+ * cordic_calc_iq() - calculates the i/q coordinate for given angle
+ *
+ * theta: angle in degrees for which i/q coordinate is to be calculated
+ * coord: function output parameter holding the i/q coordinate
+ */
+struct cordic_iq cordic_calc_iq(s32 theta)
+{
+ struct cordic_iq coord;
+ s32 angle, valtmp;
+ unsigned iter;
+ int signx = 1;
+ int signtheta;
+
+ coord.i = CORDIC_ANGLE_GEN;
+ coord.q = 0;
+ angle = 0;
+
+ theta = FIXED(theta);
+ signtheta = (theta < 0) ? -1 : 1;
+ theta = ((theta + FIXED(180) * signtheta) % FIXED(360)) -
+ FIXED(180) * signtheta;
+
+ if (FLOAT(theta) > 90) {
+ theta -= FIXED(180);
+ signx = -1;
+ } else if (FLOAT(theta) < -90) {
+ theta += FIXED(180);
+ signx = -1;
+ }
+
+ for (iter = 0; iter < CORDIC_NUM_ITER; iter++) {
+ if (theta > angle) {
+ valtmp = coord.i - (coord.q >> iter);
+ coord.q += (coord.i >> iter);
+ angle += arctan_table[iter];
+ } else {
+ valtmp = coord.i + (coord.q >> iter);
+ coord.q -= (coord.i >> iter);
+ angle -= arctan_table[iter];
+ }
+ coord.i = valtmp;
+ }
+
+ coord.i *= signx;
+ coord.q *= signx;
+ return coord;
+}
+EXPORT_SYMBOL(cordic_calc_iq);
+
+MODULE_DESCRIPTION("Cordic functions");
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/compat/crc8.c b/compat/crc8.c
new file mode 100644
index 0000000..87b59ca
--- /dev/null
+++ b/compat/crc8.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2011 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/crc8.h>
+#include <linux/printk.h>
+
+/*
+ * crc8_populate_msb - fill crc table for given polynomial in reverse bit order.
+ *
+ * table: table to be filled.
+ * polynomial: polynomial for which table is to be filled.
+ */
+void crc8_populate_msb(u8 table[CRC8_TABLE_SIZE], u8 polynomial)
+{
+ int i, j;
+ const u8 msbit = 0x80;
+ u8 t = msbit;
+
+ table[0] = 0;
+
+ for (i = 1; i < CRC8_TABLE_SIZE; i *= 2) {
+ t = (t << 1) ^ (t & msbit ? polynomial : 0);
+ for (j = 0; j < i; j++)
+ table[i+j] = table[j] ^ t;
+ }
+}
+EXPORT_SYMBOL(crc8_populate_msb);
+
+/*
+ * crc8_populate_lsb - fill crc table for given polynomial in regular bit order.
+ *
+ * table: table to be filled.
+ * polynomial: polynomial for which table is to be filled.
+ */
+void crc8_populate_lsb(u8 table[CRC8_TABLE_SIZE], u8 polynomial)
+{
+ int i, j;
+ u8 t = 1;
+
+ table[0] = 0;
+
+ for (i = (CRC8_TABLE_SIZE >> 1); i; i >>= 1) {
+ t = (t >> 1) ^ (t & 1 ? polynomial : 0);
+ for (j = 0; j < CRC8_TABLE_SIZE; j += 2*i)
+ table[i+j] = table[j] ^ t;
+ }
+}
+EXPORT_SYMBOL(crc8_populate_lsb);
+
+/*
+ * crc8 - calculate a crc8 over the given input data.
+ *
+ * table: crc table used for calculation.
+ * pdata: pointer to data buffer.
+ * nbytes: number of bytes in data buffer.
+ * crc: previous returned crc8 value.
+ */
+u8 crc8(const u8 table[CRC8_TABLE_SIZE], u8 *pdata, size_t nbytes, u8 crc)
+{
+ /* loop over the buffer data */
+ while (nbytes-- > 0)
+ crc = table[(crc ^ *pdata++) & 0xff];
+
+ return crc;
+}
+EXPORT_SYMBOL(crc8);
+
+MODULE_DESCRIPTION("CRC8 (by Williams, Ross N.) function");
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/compat/kfifo.c b/compat/kfifo.c
new file mode 100644
index 0000000..01a0700
--- /dev/null
+++ b/compat/kfifo.c
@@ -0,0 +1,608 @@
+/*
+ * A generic kernel FIFO implementation
+ *
+ * Copyright (C) 2009/2010 Stefani Seibold <stefani@seibold.net>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/log2.h>
+#include <linux/uaccess.h>
+#include <linux/kfifo.h>
+
+/*
+ * internal helper to calculate the unused elements in a fifo
+ */
+static inline unsigned int kfifo_unused(struct __kfifo *fifo)
+{
+ return (fifo->mask + 1) - (fifo->in - fifo->out);
+}
+
+int __kfifo_alloc(struct __kfifo *fifo, unsigned int size,
+ size_t esize, gfp_t gfp_mask)
+{
+ /*
+ * round down to the next power of 2, since our 'let the indices
+ * wrap' technique works only in this case.
+ */
+ if (!is_power_of_2(size))
+ size = rounddown_pow_of_two(size);
+
+ fifo->in = 0;
+ fifo->out = 0;
+ fifo->esize = esize;
+
+ if (size < 2) {
+ fifo->data = NULL;
+ fifo->mask = 0;
+ return -EINVAL;
+ }
+
+ fifo->data = kmalloc(size * esize, gfp_mask);
+
+ if (!fifo->data) {
+ fifo->mask = 0;
+ return -ENOMEM;
+ }
+ fifo->mask = size - 1;
+
+ return 0;
+}
+EXPORT_SYMBOL(__kfifo_alloc);
+
+void __kfifo_free(struct __kfifo *fifo)
+{
+ kfree(fifo->data);
+ fifo->in = 0;
+ fifo->out = 0;
+ fifo->esize = 0;
+ fifo->data = NULL;
+ fifo->mask = 0;
+}
+EXPORT_SYMBOL(__kfifo_free);
+
+int __kfifo_init(struct __kfifo *fifo, void *buffer,
+ unsigned int size, size_t esize)
+{
+ size /= esize;
+
+ if (!is_power_of_2(size))
+ size = rounddown_pow_of_two(size);
+
+ fifo->in = 0;
+ fifo->out = 0;
+ fifo->esize = esize;
+ fifo->data = buffer;
+
+ if (size < 2) {
+ fifo->mask = 0;
+ return -EINVAL;
+ }
+ fifo->mask = size - 1;
+
+ return 0;
+}
+EXPORT_SYMBOL(__kfifo_init);
+
+static void kfifo_copy_in(struct __kfifo *fifo, const void *src,
+ unsigned int len, unsigned int off)
+{
+ unsigned int size = fifo->mask + 1;
+ unsigned int esize = fifo->esize;
+ unsigned int l;
+
+ off &= fifo->mask;
+ if (esize != 1) {
+ off *= esize;
+ size *= esize;
+ len *= esize;
+ }
+ l = min(len, size - off);
+
+ memcpy(fifo->data + off, src, l);
+ memcpy(fifo->data, src + l, len - l);
+ /*
+ * make sure that the data in the fifo is up to date before
+ * incrementing the fifo->in index counter
+ */
+ smp_wmb();
+}
+
+unsigned int __kfifo_in(struct __kfifo *fifo,
+ const void *buf, unsigned int len)
+{
+ unsigned int l;
+
+ l = kfifo_unused(fifo);
+ if (len > l)
+ len = l;
+
+ kfifo_copy_in(fifo, buf, len, fifo->in);
+ fifo->in += len;
+ return len;
+}
+EXPORT_SYMBOL(__kfifo_in);
+
+static void kfifo_copy_out(struct __kfifo *fifo, void *dst,
+ unsigned int len, unsigned int off)
+{
+ unsigned int size = fifo->mask + 1;
+ unsigned int esize = fifo->esize;
+ unsigned int l;
+
+ off &= fifo->mask;
+ if (esize != 1) {
+ off *= esize;
+ size *= esize;
+ len *= esize;
+ }
+ l = min(len, size - off);
+
+ memcpy(dst, fifo->data + off, l);
+ memcpy(dst + l, fifo->data, len - l);
+ /*
+ * make sure that the data is copied before
+ * incrementing the fifo->out index counter
+ */
+ smp_wmb();
+}
+
+unsigned int __kfifo_out_peek(struct __kfifo *fifo,
+ void *buf, unsigned int len)
+{
+ unsigned int l;
+
+ l = fifo->in - fifo->out;
+ if (len > l)
+ len = l;
+
+ kfifo_copy_out(fifo, buf, len, fifo->out);
+ return len;
+}
+EXPORT_SYMBOL(__kfifo_out_peek);
+
+unsigned int __kfifo_out(struct __kfifo *fifo,
+ void *buf, unsigned int len)
+{
+ len = __kfifo_out_peek(fifo, buf, len);
+ fifo->out += len;
+ return len;
+}
+EXPORT_SYMBOL(__kfifo_out);
+
+static unsigned long kfifo_copy_from_user(struct __kfifo *fifo,
+ const void __user *from, unsigned int len, unsigned int off,
+ unsigned int *copied)
+{
+ unsigned int size = fifo->mask + 1;
+ unsigned int esize = fifo->esize;
+ unsigned int l;
+ unsigned long ret;
+
+ off &= fifo->mask;
+ if (esize != 1) {
+ off *= esize;
+ size *= esize;
+ len *= esize;
+ }
+ l = min(len, size - off);
+
+ ret = copy_from_user(fifo->data + off, from, l);
+ if (unlikely(ret))
+ ret = DIV_ROUND_UP(ret + len - l, esize);
+ else {
+ ret = copy_from_user(fifo->data, from + l, len - l);
+ if (unlikely(ret))
+ ret = DIV_ROUND_UP(ret, esize);
+ }
+ /*
+ * make sure that the data in the fifo is up to date before
+ * incrementing the fifo->in index counter
+ */
+ smp_wmb();
+ *copied = len - ret;
+ /* return the number of elements which are not copied */
+ return ret;
+}
+
+int __kfifo_from_user(struct __kfifo *fifo, const void __user *from,
+ unsigned long len, unsigned int *copied)
+{
+ unsigned int l;
+ unsigned long ret;
+ unsigned int esize = fifo->esize;
+ int err;
+
+ if (esize != 1)
+ len /= esize;
+
+ l = kfifo_unused(fifo);
+ if (len > l)
+ len = l;
+
+ ret = kfifo_copy_from_user(fifo, from, len, fifo->in, copied);
+ if (unlikely(ret)) {
+ len -= ret;
+ err = -EFAULT;
+ } else
+ err = 0;
+ fifo->in += len;
+ return err;
+}
+EXPORT_SYMBOL(__kfifo_from_user);
+
+static unsigned long kfifo_copy_to_user(struct __kfifo *fifo, void __user *to,
+ unsigned int len, unsigned int off, unsigned int *copied)
+{
+ unsigned int l;
+ unsigned long ret;
+ unsigned int size = fifo->mask + 1;
+ unsigned int esize = fifo->esize;
+
+ off &= fifo->mask;
+ if (esize != 1) {
+ off *= esize;
+ size *= esize;
+ len *= esize;
+ }
+ l = min(len, size - off);
+
+ ret = copy_to_user(to, fifo->data + off, l);
+ if (unlikely(ret))
+ ret = DIV_ROUND_UP(ret + len - l, esize);
+ else {
+ ret = copy_to_user(to + l, fifo->data, len - l);
+ if (unlikely(ret))
+ ret = DIV_ROUND_UP(ret, esize);
+ }
+ /*
+ * make sure that the data is copied before
+ * incrementing the fifo->out index counter
+ */
+ smp_wmb();
+ *copied = len - ret;
+ /* return the number of elements which are not copied */
+ return ret;
+}
+
+int __kfifo_to_user(struct __kfifo *fifo, void __user *to,
+ unsigned long len, unsigned int *copied)
+{
+ unsigned int l;
+ unsigned long ret;
+ unsigned int esize = fifo->esize;
+ int err;
+
+ if (esize != 1)
+ len /= esize;
+
+ l = fifo->in - fifo->out;
+ if (len > l)
+ len = l;
+ ret = kfifo_copy_to_user(fifo, to, len, fifo->out, copied);
+ if (unlikely(ret)) {
+ len -= ret;
+ err = -EFAULT;
+ } else
+ err = 0;
+ fifo->out += len;
+ return err;
+}
+EXPORT_SYMBOL(__kfifo_to_user);
+
+static int setup_sgl_buf(struct scatterlist *sgl, void *buf,
+ int nents, unsigned int len)
+{
+ int n;
+ unsigned int l;
+ unsigned int off;
+ struct page *page;
+
+ if (!nents)
+ return 0;
+
+ if (!len)
+ return 0;
+
+ n = 0;
+ page = virt_to_page(buf);
+ off = offset_in_page(buf);
+ l = 0;
+
+ while (len >= l + PAGE_SIZE - off) {
+ struct page *npage;
+
+ l += PAGE_SIZE;
+ buf += PAGE_SIZE;
+ npage = virt_to_page(buf);
+ if (page_to_phys(page) != page_to_phys(npage) - l) {
+ sg_set_page(sgl, page, l - off, off);
+ sgl = sg_next(sgl);
+ if (++n == nents || sgl == NULL)
+ return n;
+ page = npage;
+ len -= l - off;
+ l = off = 0;
+ }
+ }
+ sg_set_page(sgl, page, len, off);
+ return n + 1;
+}
+
+static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl,
+ int nents, unsigned int len, unsigned int off)
+{
+ unsigned int size = fifo->mask + 1;
+ unsigned int esize = fifo->esize;
+ unsigned int l;
+ unsigned int n;
+
+ off &= fifo->mask;
+ if (esize != 1) {
+ off *= esize;
+ size *= esize;
+ len *= esize;
+ }
+ l = min(len, size - off);
+
+ n = setup_sgl_buf(sgl, fifo->data + off, nents, l);
+ n += setup_sgl_buf(sgl + n, fifo->data, nents - n, len - l);
+
+ return n;
+}
+
+unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo,
+ struct scatterlist *sgl, int nents, unsigned int len)
+{
+ unsigned int l;
+
+ l = kfifo_unused(fifo);
+ if (len > l)
+ len = l;
+
+ return setup_sgl(fifo, sgl, nents, len, fifo->in);
+}
+EXPORT_SYMBOL(__kfifo_dma_in_prepare);
+
+unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo,
+ struct scatterlist *sgl, int nents, unsigned int len)
+{
+ unsigned int l;
+
+ l = fifo->in - fifo->out;
+ if (len > l)
+ len = l;
+
+ return setup_sgl(fifo, sgl, nents, len, fifo->out);
+}
+EXPORT_SYMBOL(__kfifo_dma_out_prepare);
+
+unsigned int __kfifo_max_r(unsigned int len, size_t recsize)
+{
+ unsigned int max = (1 << (recsize << 3)) - 1;
+
+ if (len > max)
+ return max;
+ return len;
+}
+
+#define __KFIFO_PEEK(data, out, mask) \
+ ((data)[(out) & (mask)])
+/*
+ * __kfifo_peek_n internal helper function for determinate the length of
+ * the next record in the fifo
+ */
+static unsigned int __kfifo_peek_n(struct __kfifo *fifo, size_t recsize)
+{
+ unsigned int l;
+ unsigned int mask = fifo->mask;
+ unsigned char *data = fifo->data;
+
+ l = __KFIFO_PEEK(data, fifo->out, mask);
+
+ if (--recsize)
+ l |= __KFIFO_PEEK(data, fifo->out + 1, mask) << 8;
+
+ return l;
+}
+
+#define __KFIFO_POKE(data, in, mask, val) \
+ ( \
+ (data)[(in) & (mask)] = (unsigned char)(val) \
+ )
+
+/*
+ * __kfifo_poke_n internal helper function for storeing the length of
+ * the record into the fifo
+ */
+static void __kfifo_poke_n(struct __kfifo *fifo, unsigned int n, size_t recsize)
+{
+ unsigned int mask = fifo->mask;
+ unsigned char *data = fifo->data;
+
+ __KFIFO_POKE(data, fifo->in, mask, n);
+
+ if (recsize > 1)
+ __KFIFO_POKE(data, fifo->in + 1, mask, n >> 8);
+}
+
+unsigned int __kfifo_len_r(struct __kfifo *fifo, size_t recsize)
+{
+ return __kfifo_peek_n(fifo, recsize);
+}
+EXPORT_SYMBOL(__kfifo_len_r);
+
+unsigned int __kfifo_in_r(struct __kfifo *fifo, const void *buf,
+ unsigned int len, size_t recsize)
+{
+ if (len + recsize > kfifo_unused(fifo))
+ return 0;
+
+ __kfifo_poke_n(fifo, len, recsize);
+
+ kfifo_copy_in(fifo, buf, len, fifo->in + recsize);
+ fifo->in += len + recsize;
+ return len;
+}
+EXPORT_SYMBOL(__kfifo_in_r);
+
+static unsigned int kfifo_out_copy_r(struct __kfifo *fifo,
+ void *buf, unsigned int len, size_t recsize, unsigned int *n)
+{
+ *n = __kfifo_peek_n(fifo, recsize);
+
+ if (len > *n)
+ len = *n;
+
+ kfifo_copy_out(fifo, buf, len, fifo->out + recsize);
+ return len;
+}
+
+unsigned int __kfifo_out_peek_r(struct __kfifo *fifo, void *buf,
+ unsigned int len, size_t recsize)
+{
+ unsigned int n;
+
+ if (fifo->in == fifo->out)
+ return 0;
+
+ return kfifo_out_copy_r(fifo, buf, len, recsize, &n);
+}
+EXPORT_SYMBOL(__kfifo_out_peek_r);
+
+unsigned int __kfifo_out_r(struct __kfifo *fifo, void *buf,
+ unsigned int len, size_t recsize)
+{
+ unsigned int n;
+
+ if (fifo->in == fifo->out)
+ return 0;
+
+ len = kfifo_out_copy_r(fifo, buf, len, recsize, &n);
+ fifo->out += n + recsize;
+ return len;
+}
+EXPORT_SYMBOL(__kfifo_out_r);
+
+void __kfifo_skip_r(struct __kfifo *fifo, size_t recsize)
+{
+ unsigned int n;
+
+ n = __kfifo_peek_n(fifo, recsize);
+ fifo->out += n + recsize;
+}
+EXPORT_SYMBOL(__kfifo_skip_r);
+
+int __kfifo_from_user_r(struct __kfifo *fifo, const void __user *from,
+ unsigned long len, unsigned int *copied, size_t recsize)
+{
+ unsigned long ret;
+
+ len = __kfifo_max_r(len, recsize);
+
+ if (len + recsize > kfifo_unused(fifo)) {
+ *copied = 0;
+ return 0;
+ }
+
+ __kfifo_poke_n(fifo, len, recsize);
+
+ ret = kfifo_copy_from_user(fifo, from, len, fifo->in + recsize, copied);
+ if (unlikely(ret)) {
+ *copied = 0;
+ return -EFAULT;
+ }
+ fifo->in += len + recsize;
+ return 0;
+}
+EXPORT_SYMBOL(__kfifo_from_user_r);
+
+int __kfifo_to_user_r(struct __kfifo *fifo, void __user *to,
+ unsigned long len, unsigned int *copied, size_t recsize)
+{
+ unsigned long ret;
+ unsigned int n;
+
+ if (fifo->in == fifo->out) {
+ *copied = 0;
+ return 0;
+ }
+
+ n = __kfifo_peek_n(fifo, recsize);
+ if (len > n)
+ len = n;
+
+ ret = kfifo_copy_to_user(fifo, to, len, fifo->out + recsize, copied);
+ if (unlikely(ret)) {
+ *copied = 0;
+ return -EFAULT;
+ }
+ fifo->out += n + recsize;
+ return 0;
+}
+EXPORT_SYMBOL(__kfifo_to_user_r);
+
+unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo,
+ struct scatterlist *sgl, int nents, unsigned int len, size_t recsize)
+{
+ if (!nents)
+ BUG();
+
+ len = __kfifo_max_r(len, recsize);
+
+ if (len + recsize > kfifo_unused(fifo))
+ return 0;
+
+ return setup_sgl(fifo, sgl, nents, len, fifo->in + recsize);
+}
+EXPORT_SYMBOL(__kfifo_dma_in_prepare_r);
+
+void __kfifo_dma_in_finish_r(struct __kfifo *fifo,
+ unsigned int len, size_t recsize)
+{
+ len = __kfifo_max_r(len, recsize);
+ __kfifo_poke_n(fifo, len, recsize);
+ fifo->in += len + recsize;
+}
+EXPORT_SYMBOL(__kfifo_dma_in_finish_r);
+
+unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo,
+ struct scatterlist *sgl, int nents, unsigned int len, size_t recsize)
+{
+ if (!nents)
+ BUG();
+
+ len = __kfifo_max_r(len, recsize);
+
+ if (len + recsize > fifo->in - fifo->out)
+ return 0;
+
+ return setup_sgl(fifo, sgl, nents, len, fifo->out + recsize);
+}
+EXPORT_SYMBOL(__kfifo_dma_out_prepare_r);
+
+void __kfifo_dma_out_finish_r(struct __kfifo *fifo, size_t recsize)
+{
+ unsigned int len;
+
+ len = __kfifo_peek_n(fifo, recsize);
+ fifo->out += len + recsize;
+}
+EXPORT_SYMBOL(__kfifo_dma_out_finish_r);
diff --git a/compat/kstrtox.c b/compat/kstrtox.c
new file mode 100644
index 0000000..10aa91b
--- /dev/null
+++ b/compat/kstrtox.c
@@ -0,0 +1,236 @@
+/*
+ * Convert integer string representation to an integer.
+ * If an integer doesn't fit into specified type, -E is returned.
+ *
+ * Integer starts with optional sign.
+ * kstrtou*() functions do not accept sign "-".
+ *
+ * Radix 0 means autodetection: leading "0x" implies radix 16,
+ * leading "0" implies radix 8, otherwise radix is 10.
+ * Autodetection hints work after optional sign, but not before.
+ *
+ * If -E is returned, result is not touched.
+ */
+#include <linux/kernel.h>
+/*
+ * kstrto* was included in kernel 2.6.38.4 and causes conflicts with the
+ * version included in compat-wireless. We use strict_strtol to check if
+ * kstrto* is already available.
+ */
+#ifndef strict_strtol
+
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+static inline char _tolower(const char c)
+{
+ return c | 0x20;
+}
+
+static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
+{
+ unsigned long long acc;
+ int ok;
+
+ if (base == 0) {
+ if (s[0] == '0') {
+ if (_tolower(s[1]) == 'x' && isxdigit(s[2]))
+ base = 16;
+ else
+ base = 8;
+ } else
+ base = 10;
+ }
+ if (base == 16 && s[0] == '0' && _tolower(s[1]) == 'x')
+ s += 2;
+
+ acc = 0;
+ ok = 0;
+ while (*s) {
+ unsigned int val;
+
+ if ('0' <= *s && *s <= '9')
+ val = *s - '0';
+ else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f')
+ val = _tolower(*s) - 'a' + 10;
+ else if (*s == '\n') {
+ if (*(s + 1) == '\0')
+ break;
+ else
+ return -EINVAL;
+ } else
+ return -EINVAL;
+
+ if (val >= base)
+ return -EINVAL;
+ if (acc > div_u64(ULLONG_MAX - val, base))
+ return -ERANGE;
+ acc = acc * base + val;
+ ok = 1;
+
+ s++;
+ }
+ if (!ok)
+ return -EINVAL;
+ *res = acc;
+ return 0;
+}
+
+int kstrtoull(const char *s, unsigned int base, unsigned long long *res)
+{
+ if (s[0] == '+')
+ s++;
+ return _kstrtoull(s, base, res);
+}
+EXPORT_SYMBOL(kstrtoull);
+
+int kstrtoll(const char *s, unsigned int base, long long *res)
+{
+ unsigned long long tmp;
+ int rv;
+
+ if (s[0] == '-') {
+ rv = _kstrtoull(s + 1, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if ((long long)(-tmp) >= 0)
+ return -ERANGE;
+ *res = -tmp;
+ } else {
+ rv = kstrtoull(s, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if ((long long)tmp < 0)
+ return -ERANGE;
+ *res = tmp;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(kstrtoll);
+
+/* Internal, do not use. */
+int _kstrtoul(const char *s, unsigned int base, unsigned long *res)
+{
+ unsigned long long tmp;
+ int rv;
+
+ rv = kstrtoull(s, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if (tmp != (unsigned long long)(unsigned long)tmp)
+ return -ERANGE;
+ *res = tmp;
+ return 0;
+}
+EXPORT_SYMBOL(_kstrtoul);
+
+/* Internal, do not use. */
+int _kstrtol(const char *s, unsigned int base, long *res)
+{
+ long long tmp;
+ int rv;
+
+ rv = kstrtoll(s, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if (tmp != (long long)(long)tmp)
+ return -ERANGE;
+ *res = tmp;
+ return 0;
+}
+EXPORT_SYMBOL(_kstrtol);
+
+int kstrtouint(const char *s, unsigned int base, unsigned int *res)
+{
+ unsigned long long tmp;
+ int rv;
+
+ rv = kstrtoull(s, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if (tmp != (unsigned long long)(unsigned int)tmp)
+ return -ERANGE;
+ *res = tmp;
+ return 0;
+}
+EXPORT_SYMBOL(kstrtouint);
+
+int kstrtoint(const char *s, unsigned int base, int *res)
+{
+ long long tmp;
+ int rv;
+
+ rv = kstrtoll(s, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if (tmp != (long long)(int)tmp)
+ return -ERANGE;
+ *res = tmp;
+ return 0;
+}
+EXPORT_SYMBOL(kstrtoint);
+
+int kstrtou16(const char *s, unsigned int base, u16 *res)
+{
+ unsigned long long tmp;
+ int rv;
+
+ rv = kstrtoull(s, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if (tmp != (unsigned long long)(u16)tmp)
+ return -ERANGE;
+ *res = tmp;
+ return 0;
+}
+EXPORT_SYMBOL(kstrtou16);
+
+int kstrtos16(const char *s, unsigned int base, s16 *res)
+{
+ long long tmp;
+ int rv;
+
+ rv = kstrtoll(s, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if (tmp != (long long)(s16)tmp)
+ return -ERANGE;
+ *res = tmp;
+ return 0;
+}
+EXPORT_SYMBOL(kstrtos16);
+
+int kstrtou8(const char *s, unsigned int base, u8 *res)
+{
+ unsigned long long tmp;
+ int rv;
+
+ rv = kstrtoull(s, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if (tmp != (unsigned long long)(u8)tmp)
+ return -ERANGE;
+ *res = tmp;
+ return 0;
+}
+EXPORT_SYMBOL(kstrtou8);
+
+int kstrtos8(const char *s, unsigned int base, s8 *res)
+{
+ long long tmp;
+ int rv;
+
+ rv = kstrtoll(s, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if (tmp != (long long)(s8)tmp)
+ return -ERANGE;
+ *res = tmp;
+ return 0;
+}
+EXPORT_SYMBOL(kstrtos8);
+#endif /* #ifndef strict_strtol */
diff --git a/compat/main.c b/compat/main.c
new file mode 100644
index 0000000..f830488
--- /dev/null
+++ b/compat/main.c
@@ -0,0 +1,61 @@
+#include <linux/module.h>
+
+MODULE_AUTHOR("Luis R. Rodriguez");
+MODULE_DESCRIPTION("Kernel compatibility module");
+MODULE_LICENSE("GPL");
+
+#ifndef COMPAT_BASE_TREE
+#error "You need a COMPAT_BASE_TREE"
+#endif
+
+#ifndef COMPAT_BASE_TREE_VERSION
+#error "You need a COMPAT_BASE_TREE_VERSION"
+#endif
+
+#ifndef COMPAT_VERSION
+#error "You need a COMPAT_VERSION"
+#endif
+
+static char *compat_base_tree = COMPAT_BASE_TREE;
+static char *compat_base_tree_version = COMPAT_BASE_TREE_VERSION;
+static char *compat_version = COMPAT_VERSION;
+
+module_param(compat_base_tree, charp, 0400);
+MODULE_PARM_DESC(compat_base_tree,
+ "The upstream tree used as base for this backport");
+
+module_param(compat_base_tree_version, charp, 0400);
+MODULE_PARM_DESC(compat_base_tree_version,
+ "The git-describe of the upstream base tree");
+
+module_param(compat_version, charp, 0400);
+MODULE_PARM_DESC(compat_version,
+ "Version of the kernel compat backport work");
+
+static int __init compat_init(void)
+{
+ /* pm-qos for kernels <= 2.6.24, this is a no-op on newer kernels */
+ compat_pm_qos_power_init();
+ compat_system_workqueue_create();
+
+ printk(KERN_INFO
+ COMPAT_PROJECT " backport release: "
+ COMPAT_VERSION
+ "\n");
+ printk(KERN_INFO "Backport based on "
+ COMPAT_BASE_TREE " " COMPAT_BASE_TREE_VERSION
+ "\n");
+
+ return 0;
+}
+module_init(compat_init);
+
+static void __exit compat_exit(void)
+{
+ compat_pm_qos_power_deinit();
+ compat_system_workqueue_destroy();
+
+ return;
+}
+module_exit(compat_exit);
+
diff --git a/compat/pm_qos_params.c b/compat/pm_qos_params.c
new file mode 100644
index 0000000..833d98c
--- /dev/null
+++ b/compat/pm_qos_params.c
@@ -0,0 +1,477 @@
+#include <net/compat.h>
+
+/* This is the backport of pm-qos params for kernels <= 2.6.25 */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25))
+
+/*
+ * This module exposes the interface to kernel space for specifying
+ * QoS dependencies. It provides infrastructure for registration of:
+ *
+ * Dependents on a QoS value : register requirements
+ * Watchers of QoS value : get notified when target QoS value changes
+ *
+ * This QoS design is best effort based. Dependents register their QoS needs.
+ * Watchers register to keep track of the current QoS needs of the system.
+ *
+ * There are 3 basic classes of QoS parameter: latency, timeout, throughput
+ * each have defined units:
+ * latency: usec
+ * timeout: usec <-- currently not used.
+ * throughput: kbs (kilo byte / sec)
+ *
+ * There are lists of pm_qos_objects each one wrapping requirements, notifiers
+ *
+ * User mode requirements on a QOS parameter register themselves to the
+ * subsystem by opening the device node /dev/... and writing there request to
+ * the node. As long as the process holds a file handle open to the node the
+ * client continues to be accounted for. Upon file release the usermode
+ * requirement is removed and a new qos target is computed. This way when the
+ * requirement that the application has is cleaned up when closes the file
+ * pointer or exits the pm_qos_object will get an opportunity to clean up.
+ *
+ * Mark Gross <mgross@linux.intel.com>
+ */
+
+#include <linux/pm_qos_params.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+
+#include <linux/uaccess.h>
+
+/*
+ * locking rule: all changes to requirements or notifiers lists
+ * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock
+ * held, taken with _irqsave. One lock to rule them all
+ */
+struct requirement_list {
+ struct list_head list;
+ union {
+ s32 value;
+ s32 usec;
+ s32 kbps;
+ };
+ char *name;
+};
+
+static s32 max_compare(s32 v1, s32 v2);
+static s32 min_compare(s32 v1, s32 v2);
+
+struct pm_qos_object {
+ struct requirement_list requirements;
+ struct blocking_notifier_head *notifiers;
+ struct miscdevice pm_qos_power_miscdev;
+ char *name;
+ s32 default_value;
+ atomic_t target_value;
+ s32 (*comparitor)(s32, s32);
+};
+
+static struct pm_qos_object null_pm_qos;
+static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
+static struct pm_qos_object cpu_dma_pm_qos = {
+ .requirements = {LIST_HEAD_INIT(cpu_dma_pm_qos.requirements.list)},
+ .notifiers = &cpu_dma_lat_notifier,
+ .name = "cpu_dma_latency",
+ .default_value = 2000 * USEC_PER_SEC,
+ .target_value = ATOMIC_INIT(2000 * USEC_PER_SEC),
+ .comparitor = min_compare
+};
+
+static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
+static struct pm_qos_object network_lat_pm_qos = {
+ .requirements = {LIST_HEAD_INIT(network_lat_pm_qos.requirements.list)},
+ .notifiers = &network_lat_notifier,
+ .name = "network_latency",
+ .default_value = 2000 * USEC_PER_SEC,
+ .target_value = ATOMIC_INIT(2000 * USEC_PER_SEC),
+ .comparitor = min_compare
+};
+
+
+static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
+static struct pm_qos_object network_throughput_pm_qos = {
+ .requirements =
+ {LIST_HEAD_INIT(network_throughput_pm_qos.requirements.list)},
+ .notifiers = &network_throughput_notifier,
+ .name = "network_throughput",
+ .default_value = 0,
+ .target_value = ATOMIC_INIT(0),
+ .comparitor = max_compare
+};
+
+static BLOCKING_NOTIFIER_HEAD(system_bus_freq_notifier);
+static struct pm_qos_object system_bus_freq_pm_qos = {
+ .requirements =
+ {LIST_HEAD_INIT(system_bus_freq_pm_qos.requirements.list)},
+ .notifiers = &system_bus_freq_notifier,
+ .name = "system_bus_freq",
+ .default_value = 0,
+ .target_value = ATOMIC_INIT(0),
+ .comparitor = max_compare
+};
+
+
+static struct pm_qos_object *pm_qos_array[PM_QOS_NUM_CLASSES] = {
+ [PM_QOS_RESERVED] = &null_pm_qos,
+ [PM_QOS_CPU_DMA_LATENCY] = &cpu_dma_pm_qos,
+ [PM_QOS_NETWORK_LATENCY] = &network_lat_pm_qos,
+ [PM_QOS_NETWORK_THROUGHPUT] = &network_throughput_pm_qos,
+ [PM_QOS_SYSTEM_BUS_FREQ] = &system_bus_freq_pm_qos,
+};
+
+static DEFINE_SPINLOCK(pm_qos_lock);
+
+static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos);
+static int pm_qos_power_open(struct inode *inode, struct file *filp);
+static int pm_qos_power_release(struct inode *inode, struct file *filp);
+
+static const struct file_operations pm_qos_power_fops = {
+ .write = pm_qos_power_write,
+ .open = pm_qos_power_open,
+ .release = pm_qos_power_release,
+};
+
+/* static helper functions */
+static s32 max_compare(s32 v1, s32 v2)
+{
+ return max(v1, v2);
+}
+
+static s32 min_compare(s32 v1, s32 v2)
+{
+ return min(v1, v2);
+}
+
+
+static void update_target(int target)
+{
+ s32 extreme_value;
+ struct requirement_list *node;
+ unsigned long flags;
+ int call_notifier = 0;
+
+ spin_lock_irqsave(&pm_qos_lock, flags);
+ extreme_value = pm_qos_array[target]->default_value;
+ list_for_each_entry(node,
+ &pm_qos_array[target]->requirements.list, list) {
+ extreme_value = pm_qos_array[target]->comparitor(
+ extreme_value, node->value);
+ }
+ if (atomic_read(&pm_qos_array[target]->target_value) != extreme_value) {
+ call_notifier = 1;
+ atomic_set(&pm_qos_array[target]->target_value, extreme_value);
+ pr_debug(KERN_ERR "new target for qos %d is %d\n", target,
+ atomic_read(&pm_qos_array[target]->target_value));
+ }
+ spin_unlock_irqrestore(&pm_qos_lock, flags);
+
+ if (call_notifier)
+ blocking_notifier_call_chain(pm_qos_array[target]->notifiers,
+ (unsigned long) extreme_value, NULL);
+}
+
+static int register_pm_qos_misc(struct pm_qos_object *qos)
+{
+ qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
+ qos->pm_qos_power_miscdev.name = qos->name;
+ qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
+
+ return misc_register(&qos->pm_qos_power_miscdev);
+}
+
+static int find_pm_qos_object_by_minor(int minor)
+{
+ int pm_qos_class;
+
+ for (pm_qos_class = 0;
+ pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) {
+ if (minor ==
+ pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor)
+ return pm_qos_class;
+ }
+ return -1;
+}
+
+/**
+ * pm_qos_requirement - returns current system wide qos expectation
+ * @pm_qos_class: identification of which qos value is requested
+ *
+ * This function returns the current target value in an atomic manner.
+ */
+int pm_qos_requirement(int pm_qos_class)
+{
+ return atomic_read(&pm_qos_array[pm_qos_class]->target_value);
+}
+EXPORT_SYMBOL_GPL(pm_qos_requirement);
+
+/**
+ * pm_qos_add_requirement - inserts new qos request into the list
+ * @pm_qos_class: identifies which list of qos request to us
+ * @name: identifies the request
+ * @value: defines the qos request
+ *
+ * This function inserts a new entry in the pm_qos_class list of requested qos
+ * performance characteristics. It recomputes the aggregate QoS expectations
+ * for the pm_qos_class of parameters.
+ */
+int pm_qos_add_requirement(int pm_qos_class, char *name, s32 value)
+{
+ struct requirement_list *dep;
+ unsigned long flags;
+
+ dep = kzalloc(sizeof(struct requirement_list), GFP_KERNEL);
+ if (dep) {
+ if (value == PM_QOS_DEFAULT_VALUE)
+ dep->value = pm_qos_array[pm_qos_class]->default_value;
+ else
+ dep->value = value;
+ dep->name = kstrdup(name, GFP_KERNEL);
+ if (!dep->name)
+ goto cleanup;
+
+ spin_lock_irqsave(&pm_qos_lock, flags);
+ list_add(&dep->list,
+ &pm_qos_array[pm_qos_class]->requirements.list);
+ spin_unlock_irqrestore(&pm_qos_lock, flags);
+ update_target(pm_qos_class);
+
+ return 0;
+ }
+
+cleanup:
+ kfree(dep);
+ return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(pm_qos_add_requirement);
+
+/**
+ * pm_qos_update_requirement - modifies an existing qos request
+ * @pm_qos_class: identifies which list of qos request to us
+ * @name: identifies the request
+ * @value: defines the qos request
+ *
+ * Updates an existing qos requirement for the pm_qos_class of parameters along
+ * with updating the target pm_qos_class value.
+ *
+ * If the named request isn't in the list then no change is made.
+ */
+int pm_qos_update_requirement(int pm_qos_class, char *name, s32 new_value)
+{
+ unsigned long flags;
+ struct requirement_list *node;
+ int pending_update = 0;
+
+ spin_lock_irqsave(&pm_qos_lock, flags);
+ list_for_each_entry(node,
+ &pm_qos_array[pm_qos_class]->requirements.list, list) {
+ if (strcmp(node->name, name) == 0) {
+ if (new_value == PM_QOS_DEFAULT_VALUE)
+ node->value =
+ pm_qos_array[pm_qos_class]->default_value;
+ else
+ node->value = new_value;
+ pending_update = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&pm_qos_lock, flags);
+ if (pending_update)
+ update_target(pm_qos_class);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pm_qos_update_requirement);
+
+/**
+ * pm_qos_remove_requirement - modifies an existing qos request
+ * @pm_qos_class: identifies which list of qos request to us
+ * @name: identifies the request
+ *
+ * Will remove named qos request from pm_qos_class list of parameters and
+ * recompute the current target value for the pm_qos_class.
+ */
+void pm_qos_remove_requirement(int pm_qos_class, char *name)
+{
+ unsigned long flags;
+ struct requirement_list *node;
+ int pending_update = 0;
+
+ spin_lock_irqsave(&pm_qos_lock, flags);
+ list_for_each_entry(node,
+ &pm_qos_array[pm_qos_class]->requirements.list, list) {
+ if (strcmp(node->name, name) == 0) {
+ kfree(node->name);
+ list_del(&node->list);
+ kfree(node);
+ pending_update = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&pm_qos_lock, flags);
+ if (pending_update)
+ update_target(pm_qos_class);
+}
+EXPORT_SYMBOL_GPL(pm_qos_remove_requirement);
+
+/**
+ * pm_qos_add_notifier - sets notification entry for changes to target value
+ * @pm_qos_class: identifies which qos target changes should be notified.
+ * @notifier: notifier block managed by caller.
+ *
+ * will register the notifier into a notification chain that gets called
+ * upon changes to the pm_qos_class target value.
+ */
+int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier)
+{
+ int retval;
+
+ retval = blocking_notifier_chain_register(
+ pm_qos_array[pm_qos_class]->notifiers, notifier);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(pm_qos_add_notifier);
+
+/**
+ * pm_qos_remove_notifier - deletes notification entry from chain.
+ * @pm_qos_class: identifies which qos target changes are notified.
+ * @notifier: notifier block to be removed.
+ *
+ * will remove the notifier from the notification chain that gets called
+ * upon changes to the pm_qos_class target value.
+ */
+int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
+{
+ int retval;
+
+ retval = blocking_notifier_chain_unregister(
+ pm_qos_array[pm_qos_class]->notifiers, notifier);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
+
+#define PID_NAME_LEN 32
+
+static int pm_qos_power_open(struct inode *inode, struct file *filp)
+{
+ int ret;
+ long pm_qos_class;
+ char name[PID_NAME_LEN];
+
+ pm_qos_class = find_pm_qos_object_by_minor(iminor(inode));
+ if (pm_qos_class >= 0) {
+ filp->private_data = (void *)pm_qos_class;
+ snprintf(name, PID_NAME_LEN, "process_%d", current->pid);
+ ret = pm_qos_add_requirement(pm_qos_class, name,
+ PM_QOS_DEFAULT_VALUE);
+ if (ret >= 0)
+ return 0;
+ }
+ return -EPERM;
+}
+
+static int pm_qos_power_release(struct inode *inode, struct file *filp)
+{
+ int pm_qos_class;
+ char name[PID_NAME_LEN];
+
+ pm_qos_class = (long)filp->private_data;
+ snprintf(name, PID_NAME_LEN, "process_%d", current->pid);
+ pm_qos_remove_requirement(pm_qos_class, name);
+
+ return 0;
+}
+
+static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ s32 value;
+ int pm_qos_class;
+ char name[PID_NAME_LEN];
+
+ pm_qos_class = (long)filp->private_data;
+ if (count != sizeof(s32))
+ return -EINVAL;
+ if (copy_from_user(&value, buf, sizeof(s32)))
+ return -EFAULT;
+ snprintf(name, PID_NAME_LEN, "process_%d", current->pid);
+ pm_qos_update_requirement(pm_qos_class, name, value);
+
+ return sizeof(s32);
+}
+
+
+/*
+ * This initializes pm-qos for older kernels.
+ */
+int compat_pm_qos_power_init(void)
+{
+ int ret = 0;
+
+ ret = register_pm_qos_misc(&cpu_dma_pm_qos);
+ if (ret < 0) {
+ printk(KERN_ERR "pm_qos_param: cpu_dma_latency setup failed\n");
+ return ret;
+ }
+ ret = register_pm_qos_misc(&network_lat_pm_qos);
+ if (ret < 0) {
+ printk(KERN_ERR "pm_qos_param: network_latency setup failed\n");
+ return ret;
+ }
+ ret = register_pm_qos_misc(&network_throughput_pm_qos);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "pm_qos_param: network_throughput setup failed\n");
+ return ret;
+ }
+ ret = register_pm_qos_misc(&system_bus_freq_pm_qos);
+ if (ret < 0)
+ printk(KERN_ERR
+ "pm_qos_param: system_bus_freq setup failed\n");
+
+ return ret;
+}
+
+int compat_pm_qos_power_deinit(void)
+{
+ int ret = 0;
+
+ ret = misc_deregister(&cpu_dma_pm_qos.pm_qos_power_miscdev);
+ if (ret < 0) {
+ printk(KERN_ERR "pm_qos_param: cpu_dma_latency deinit failed\n");
+ return ret;
+ }
+
+ ret = misc_deregister(&network_lat_pm_qos.pm_qos_power_miscdev);
+ if (ret < 0) {
+ printk(KERN_ERR "pm_qos_param: network_latency deinit failed\n");
+ return ret;
+ }
+
+ ret = misc_deregister(&network_throughput_pm_qos.pm_qos_power_miscdev);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "pm_qos_param: network_throughput deinit failed\n");
+ return ret;
+ }
+
+ ret = misc_deregister(&system_bus_freq_pm_qos.pm_qos_power_miscdev);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "pm_qos_param: system_bus_freq deinit failed\n");
+ return ret;
+ }
+
+ return ret;
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) */
diff --git a/compat/scripts/compat_firmware_install b/compat/scripts/compat_firmware_install
new file mode 100755
index 0000000..33e4fde
--- /dev/null
+++ b/compat/scripts/compat_firmware_install
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+if [ -f /usr/bin/lsb_release ]; then
+ LSB_RED_ID=$(/usr/bin/lsb_release -i -s)
+else
+ LSB_RED_ID="Unknown"
+fi
+
+case $LSB_RED_ID in
+"Ubuntu")
+ mkdir -p /lib/udev/ /lib/udev/rules.d/
+ cp udev/ubuntu/compat_firmware.sh /lib/udev/
+ cp udev/ubuntu/50-compat_firmware.rules /lib/udev/rules.d/
+ ;;
+*)
+ mkdir -p /lib/udev/ /lib/udev/rules.d/
+ cp udev/compat_firmware.sh /lib/udev/
+ cp udev/50-compat_firmware.rules /lib/udev/rules.d/
+ ;;
+esac
+
diff --git a/compat_base_tree b/compat_base_tree
new file mode 100644
index 0000000..d6f97f5
--- /dev/null
+++ b/compat_base_tree
@@ -0,0 +1 @@
+ath6kl.git
diff --git a/compat_base_tree_version b/compat_base_tree_version
new file mode 100644
index 0000000..5d95780
--- /dev/null
+++ b/compat_base_tree_version
@@ -0,0 +1 @@
+branching-rel-3.2.3-74-g39f7005
diff --git a/compat_version b/compat_version
new file mode 100644
index 0000000..df5977e
--- /dev/null
+++ b/compat_version
@@ -0,0 +1 @@
+3.3-OSR-2012-10-11-15-g2bd3ebf
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..daad171
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,699 @@
+export
+
+## NOTE
+## Make sure to have each variable declaration start
+## in the first column, no whitespace allowed.
+
+ifeq ($(wildcard $(KLIB_BUILD)/.config),)
+# These will be ignored by compat autoconf
+ CONFIG_PCI=y
+ CONFIG_USB=y
+ CONFIG_PCMCIA=y
+ CONFIG_SSB=m
+else
+include $(KLIB_BUILD)/.config
+endif
+
+ifneq ($(wildcard $(KLIB_BUILD)/Makefile),)
+
+COMPAT_LATEST_VERSION = 3
+
+KERNEL_VERSION := $(shell $(MAKE) -C $(KLIB_BUILD) kernelversion | sed -n 's/^\([0-9]\)\..*/\1/p')
+
+ifneq ($(KERNEL_VERSION),2)
+KERNEL_SUBLEVEL := $(shell $(MAKE) -C $(KLIB_BUILD) kernelversion | sed -n 's/^3\.\([0-9]\+\).*/\1/p')
+else
+COMPAT_26LATEST_VERSION = 39
+KERNEL_26SUBLEVEL := $(shell $(MAKE) -C $(KLIB_BUILD) kernelversion | sed -n 's/^2\.6\.\([0-9]\+\).*/\1/p')
+COMPAT_26VERSIONS := $(shell I=$(COMPAT_26LATEST_VERSION); while [ "$$I" -gt $(KERNEL_26SUBLEVEL) ]; do echo $$I; I=$$(($$I - 1)); done)
+$(foreach ver,$(COMPAT_26VERSIONS),$(eval CONFIG_COMPAT_KERNEL_2_6_$(ver)=y))
+KERNEL_SUBLEVEL := -1
+endif
+
+COMPAT_VERSIONS := $(shell I=$(COMPAT_LATEST_VERSION); while [ "$$I" -gt $(KERNEL_SUBLEVEL) ]; do echo $$I; I=$$(($$I - 1)); done)
+$(foreach ver,$(COMPAT_VERSIONS),$(eval CONFIG_COMPAT_KERNEL_3_$(ver)=y))
+
+RHEL_MAJOR := $(shell grep ^RHEL_MAJOR $(KLIB_BUILD)/Makefile | sed -n 's/.*= *\(.*\)/\1/p')
+
+ifneq ($(RHEL_MAJOR),)
+RHEL_MINOR := $(shell grep ^RHEL_MINOR $(KLIB_BUILD)/Makefile | sed -n 's/.*= *\(.*\)/\1/p')
+COMPAT_RHEL_VERSIONS := $(shell I=$(RHEL_MINOR); while [ "$$I" -ge 0 ]; do echo $$I; I=$$(($$I - 1)); done)
+$(foreach ver,$(COMPAT_RHEL_VERSIONS),$(eval CONFIG_COMPAT_RHEL_$(RHEL_MAJOR)_$(ver)=y))
+endif
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_24
+$(error "ERROR: compat-wireless by default supports kernels >= 2.6.24, try enabling only one driver though")
+endif #CONFIG_COMPAT_KERNEL_2_6_24
+
+ifeq ($(CONFIG_CFG80211),y)
+$(error "ERROR: your kernel has CONFIG_CFG80211=y, you should have it CONFIG_CFG80211=m if you want to use this thing.")
+endif
+
+
+# 2.6.27 has FTRACE_DYNAMIC borked, so we will complain if
+# you have it enabled, otherwise you will very likely run into
+# a kernel panic.
+ifeq ($(shell test $(KERNEL_VERSION) -eq 2 -a $(KERNEL_SUBLEVEL) -eq 27 && echo yes),yes)
+ifeq ($(CONFIG_DYNAMIC_FTRACE),y)
+$(error "ERROR: Your 2.6.27 kernel has CONFIG_DYNAMIC_FTRACE, please upgrade your distribution kernel as newer ones should not have this enabled (and if so report a bug) or remove this warning if you know what you are doing")
+endif
+endif
+
+# This is because with CONFIG_MAC80211 include/linux/skbuff.h will
+# enable on 2.6.27 a new attribute:
+#
+# skb->do_not_encrypt
+#
+# and on 2.6.28 another new attribute:
+#
+# skb->requeue
+#
+# In kernel 2.6.32 both attributes were removed.
+#
+ifeq ($(shell test $(KERNEL_VERSION) -eq 2 -a $(KERNEL_SUBLEVEL) -ge 27 -a $(KERNEL_SUBLEVEL) -le 31 && echo yes),yes)
+ifeq ($(CONFIG_MAC80211),)
+$(error "ERROR: Your >=2.6.27 and <= 2.6.31 kernel has CONFIG_MAC80211 disabled, you should have it CONFIG_MAC80211=m if you want to use this thing.")
+endif
+endif
+
+ifneq ($(KERNELRELEASE),) # This prevents a warning
+
+# We will warn when you don't have MQ support or NET_SCHED enabled.
+#
+# We could consider just quiting if MQ and NET_SCHED is disabled
+# as I suspect all users of this package want 802.11e (WME) and
+# 802.11n (HT) support.
+ifeq ($(CONFIG_NET_SCHED),)
+ QOS_REQS_MISSING+=CONFIG_NET_SCHED
+endif
+
+ifneq ($(QOS_REQS_MISSING),) # Complain about our missing dependencies
+$(warning "WARNING: You are running a kernel >= 2.6.23, you should enable in it $(QOS_REQS_MISSING) for 802.11[ne] support")
+endif
+
+endif # build check
+endif # kernel Makefile check
+
+# These both are needed by compat-wireless || compat-bluetooth so enable them
+ CONFIG_COMPAT_RFKILL=y
+
+ifeq ($(CONFIG_MAC80211),y)
+$(error "ERROR: you have MAC80211 compiled into the kernel, CONFIG_MAC80211=y, as such you cannot replace its mac80211 driver. You need this set to CONFIG_MAC80211=m. If you are using Fedora upgrade your kernel as later version should this set as modular. For further information on Fedora see https://bugzilla.redhat.com/show_bug.cgi?id=470143. If you are using your own kernel recompile it and make mac80211 modular")
+else
+ CONFIG_COMPAT_WIRELESS=y
+ CONFIG_COMPAT_WIRELESS_MODULES=m
+ CONFIG_COMPAT_VAR_MODULES=m
+# We could technically separate these but not yet, we only have b44
+# Note that we don't intend on backporting network drivers that
+# use Multiqueue as that was a pain to backport to kernels older than
+# 2.6.27. But -- we could just disable those drivers from kernels
+# older than 2.6.27
+ CONFIG_COMPAT_NETWORK_MODULES=m
+ CONFIG_COMPAT_NET_USB_MODULES=m
+endif
+
+# The Bluetooth compatibility only builds on kernels >= 2.6.27 for now
+ifndef CONFIG_COMPAT_KERNEL_2_6_27
+ifeq ($(CONFIG_BT),y)
+# we'll ignore compiling bluetooth
+else
+ CONFIG_COMPAT_BLUETOOTH=y
+ CONFIG_COMPAT_BLUETOOTH_MODULES=m
+endif
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+#
+# CONFIG_COMPAT_FIRMWARE_CLASS definition has no leading whitespace,
+# because it gets passed-on through compat_autoconf.h.
+#
+ifdef CONFIG_COMPAT_KERNEL_2_6_33
+ifndef CONFIG_COMPAT_RHEL_6_1
+ifdef CONFIG_FW_LOADER
+CONFIG_COMPAT_FIRMWARE_CLASS=m
+endif #CONFIG_FW_LOADER
+endif #CONFIG_COMPAT_RHEL_6_1
+endif #CONFIG_COMPAT_KERNEL_2_6_33
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_36
+ifndef CONFIG_COMPAT_RHEL_6_1
+ CONFIG_COMPAT_KFIFO=y
+endif #CONFIG_COMPAT_RHEL_6_1
+endif #CONFIG_COMPAT_KERNEL_2_6_36
+
+#
+# CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN definitions have no leading
+# whitespace, because they get passed-on through compat_autoconf.h.
+#
+ifndef CONFIG_COMPAT_KERNEL_2_6_33
+CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN=y
+endif #CONFIG_COMPAT_KERNEL_2_6_33
+ifdef CONFIG_COMPAT_RHEL_6_0
+CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN=y
+endif #CONFIG_COMPAT_RHEL_6_0
+
+#
+# CONFIG_COMPAT_FIRMWARE_DATA_RW_NEEDS_FILP definition has no leading
+# whitespace, because it gets passed-on through compat_autoconf.h.
+#
+ifdef CONFIG_COMPAT_RHEL_6_0
+CONFIG_COMPAT_FIRMWARE_DATA_RW_NEEDS_FILP=y
+endif #CONFIG_COMPAT_RHEL_6_0
+
+# Wireless subsystem stuff
+CONFIG_MAC80211=m
+
+ifndef CONFIG_COMPAT_KERNEL_2_6_33
+CONFIG_MAC80211_DRIVER_API_TRACER=y
+endif #CONFIG_COMPAT_KERNEL_2_6_33
+
+# CONFIG_MAC80211_DEBUGFS=y
+# CONFIG_MAC80211_NOINLINE=y
+# CONFIG_MAC80211_VERBOSE_DEBUG=y
+# CONFIG_MAC80211_HT_DEBUG=y
+# CONFIG_MAC80211_TKIP_DEBUG=y
+# CONFIG_MAC80211_IBSS_DEBUG=y
+# CONFIG_MAC80211_VERBOSE_PS_DEBUG=y
+# CONFIG_MAC80211_VERBOSE_MPL_DEBUG=y
+# CONFIG_MAC80211_VERBOSE_MHWMP_DEBUG=y
+# CONFIG_MAC80211_VERBOSE_TDLS_DEBUG
+# CONFIG_MAC80211_DEBUG_COUNTERS=y
+
+# choose between pid and minstrel as default rate control algorithm
+CONFIG_MAC80211_RC_DEFAULT=minstrel_ht
+CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y
+# CONFIG_MAC80211_RC_DEFAULT_PID=y
+# This is the one used by our compat-wireless net/mac80211/rate.c
+# in case you have and old kernel which is overriding this to pid.
+CONFIG_COMPAT_MAC80211_RC_DEFAULT=minstrel_ht
+CONFIG_MAC80211_RC_PID=y
+CONFIG_MAC80211_RC_MINSTREL=y
+CONFIG_MAC80211_RC_MINSTREL_HT=y
+ifdef CONFIG_LEDS_TRIGGERS
+CONFIG_MAC80211_LEDS=y
+endif #CONFIG_LEDS_TRIGGERS
+
+# enable mesh networking too
+CONFIG_MAC80211_MESH=y
+
+CONFIG_CFG80211=m
+CONFIG_CFG80211_DEFAULT_PS=y
+# CONFIG_CFG80211_DEBUGFS=y
+CONFIG_NL80211_TESTMODE=y
+# CONFIG_CFG80211_DEVELOPER_WARNINGS=y
+# CONFIG_CFG80211_REG_DEBUG=y
+CONFIG_CFG80211_INTERNAL_REGDB=y
+# See below for wext stuff
+
+CONFIG_LIB80211=m
+CONFIG_LIB80211_CRYPT_WEP=m
+CONFIG_LIB80211_CRYPT_CCMP=m
+CONFIG_LIB80211_CRYPT_TKIP=m
+# CONFIG_LIB80211_DEBUG=y
+
+CONFIG_BT=m
+CONFIG_COMPAT_BT_L2CAP=y
+CONFIG_COMPAT_BT_SCO=y
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+# CONFIG_BT_CMTP depends on ISDN_CAPI
+ifdef CONFIG_ISDN_CAPI
+CONFIG_BT_CMTP=m
+endif #CONFIG_ISDN_CAPI
+ifndef CONFIG_COMPAT_KERNEL_2_6_28
+CONFIG_COMPAT_BT_HIDP=m
+endif #CONFIG_COMPAT_KERNEL_2_6_28
+
+CONFIG_BT_HCIUART=M
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+CONFIG_BT_HCIUART_ATH3K=y
+CONFIG_BT_HCIUART_LL=y
+
+CONFIG_BT_HCIVHCI=m
+CONFIG_BT_MRVL=m
+
+ifdef CONFIG_PCMCIA
+CONFIG_BT_HCIDTL1=m
+CONFIG_BT_HCIBT3C=m
+CONFIG_BT_HCIBLUECARD=m
+CONFIG_BT_HCIBTUART=m
+endif #CONFIG_PCMCIA
+
+
+# We need CONFIG_WIRELESS_EXT for CONFIG_CFG80211_WEXT for every kernel
+# version. The new way CONFIG_CFG80211_WEXT is called from the kernel
+# does not work with compat-wireless because it calls some callback
+# function on struct wiphy. This struct is shipped with compat-wireless
+# and changes from kernel version to version. We are using the
+# wireless_handlers attribute which will be activated by
+# CONFIG_WIRELESS_EXT.
+ifdef CONFIG_WIRELESS_EXT
+CONFIG_CFG80211_WEXT=y
+else #CONFIG_CFG80211_WEXT
+$(warning "WARNING: CONFIG_CFG80211_WEXT will be deactivated or not working because kernel was compiled with CONFIG_WIRELESS_EXT=n. Tools using wext interface like iwconfig will not work. To activate it build your kernel e.g. with CONFIG_LIBIPW=m.")
+endif #CONFIG_WIRELESS_EXT
+
+ifdef CONFIG_STAGING
+CONFIG_COMPAT_STAGING=m
+endif #CONFIG_STAGING
+
+# mac80211 test driver
+CONFIG_MAC80211_HWSIM=m
+
+CONFIG_ATH5K=m
+# CONFIG_ATH5K_DEBUG=y
+# CONFIG_ATH5K_TRACER=y
+# CONFIG_ATH5K_AHB=y
+
+CONFIG_ATH9K=m
+CONFIG_ATH9K_HW=m
+CONFIG_ATH9K_COMMON=m
+# CONFIG_ATH9K_DEBUGFS=y
+# CONFIG_ATH9K_AHB=y
+# CONFIG_ATH9K_PKTLOG=y
+
+# Disable this to get minstrel as default, we leave the ath9k
+# rate control algorithm as the default for now as that is also
+# default upstream on the kernel. We will move this to minstrel
+# as default once we get minstrel properly tested and blessed by
+# our systems engineering team. CCK rates also need to be used
+# for long range considerations.
+CONFIG_ATH9K_RATE_CONTROL=y
+
+# PCI Drivers
+ifdef CONFIG_PCI
+
+CONFIG_ATH5K_PCI=y
+CONFIG_ATH9K_PCI=y
+
+CONFIG_IWLWIFI=m
+# CONFIG_IWLWIFI_DEBUG=y
+# CONFIG_IWLWIFI_DEBUGFS=y
+# CONFIG_IWLWIFI_DEVICE_TRACING=y
+# CONFIG_IWLWIFI_DEVICE_SVTOOL=y
+# CONFIG_IWLWIFI_DEBUG_EXPERIMENTAL_UCODE=y
+
+CONFIG_IWLEGACY=m
+CONFIG_COMPAT_IWL4965=m
+CONFIG_IWL3945=m
+# CONFIG_IWLEGACY_DEBUG=y
+# CONFIG_IWLEGACY_DEBUGFS=y
+
+
+CONFIG_B43=m
+CONFIG_B43_HWRNG=y
+CONFIG_B43_PCI_AUTOSELECT=y
+ifdef CONFIG_PCMCIA
+CONFIG_B43_PCMCIA=y
+endif #CONFIG_PCMCIA
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_B43_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+CONFIG_B43_PHY_LP=y
+CONFIG_B43_PHY_N=y
+CONFIG_B43_PHY_HT=y
+# CONFIG_B43_PHY_LCN=y
+# CONFIG_B43_FORCE_PIO=y
+# CONFIG_B43_DEBUG=y
+
+CONFIG_B43LEGACY=m
+CONFIG_B43LEGACY_HWRNG=y
+CONFIG_B43LEGACY_PCI_AUTOSELECT=y
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_B43LEGACY_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+# CONFIG_B43LEGACY_DEBUG=y
+CONFIG_B43LEGACY_DMA=y
+CONFIG_B43LEGACY_PIO=y
+
+ifdef CONFIG_WIRELESS_EXT
+# The Intel ipws
+CONFIG_LIBIPW=m
+# CONFIG_LIBIPW_DEBUG=y
+
+CONFIG_IPW2100=m
+CONFIG_IPW2100_MONITOR=y
+# CONFIG_IPW2100_DEBUG=y
+CONFIG_IPW2200=m
+CONFIG_IPW2200_MONITOR=y
+CONFIG_IPW2200_RADIOTAP=y
+CONFIG_IPW2200_PROMISCUOUS=y
+CONFIG_IPW2200_QOS=y
+# CONFIG_IPW2200_DEBUG=y
+# The above enables use a second interface prefixed 'rtap'.
+# Example usage:
+#
+# % modprobe ipw2200 rtap_iface=1
+# % ifconfig rtap0 up
+# % tethereal -i rtap0
+#
+# If you do not specify 'rtap_iface=1' as a module parameter then
+# the rtap interface will not be created and you will need to turn
+# it on via sysfs:
+#
+# % echo 1 > /sys/bus/pci/drivers/ipw2200/*/rtap_iface
+endif #CONFIG_WIRELESS_EXT
+
+ifdef CONFIG_SSB
+# Sonics Silicon Backplane
+CONFIG_SSB_SPROM=y
+
+CONFIG_SSB_BLOCKIO=y
+CONFIG_SSB_PCIHOST=y
+CONFIG_SSB_B43_PCI_BRIDGE=y
+ifdef CONFIG_PCMCIA
+CONFIG_SSB_PCMCIAHOST=y
+endif #CONFIG_PCMCIA
+# CONFIG_SSB_DEBUG=y
+CONFIG_SSB_DRIVER_PCICORE=y
+CONFIG_B43_SSB=y
+endif #CONFIG_SSB
+
+CONFIG_BCMA=m
+CONFIG_BCMA_BLOCKIO=y
+CONFIG_BCMA_HOST_PCI=y
+# CONFIG_BCMA_DEBUG=y
+CONFIG_B43_BCMA=y
+CONFIG_B43_BCMA_PIO=y
+
+CONFIG_P54_PCI=m
+
+CONFIG_B44=m
+CONFIG_B44_PCI=y
+
+CONFIG_RTL8180=m
+
+CONFIG_ADM8211=m
+
+CONFIG_RT2X00_LIB_PCI=m
+CONFIG_RT2400PCI=m
+CONFIG_RT2500PCI=m
+ifdef CONFIG_CRC_CCITT
+CONFIG_RT2800PCI=m
+CONFIG_RT2800PCI_RT33XX=y
+CONFIG_RT2800PCI_RT35XX=y
+# CONFIG_RT2800PCI_RT53XX=y
+endif #CONFIG_CRC_CCITT
+NEED_RT2X00=y
+
+# Two rt2x00 drivers require firmware: rt61pci and rt73usb. They depend on
+# CRC to check the firmware. We check here first for the PCI
+# driver as we're in the PCI section.
+ifdef CONFIG_CRC_ITU_T
+CONFIG_RT61PCI=m
+endif #CONFIG_CRC_ITU_T
+
+CONFIG_MWL8K=m
+
+# Ethernet drivers go here
+CONFIG_ATL1=m
+CONFIG_ATL2=m
+CONFIG_ATL1E=m
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_ATL1C=m
+CONFIG_ALX=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_ATL1C=n
+CONFIG_ALX=m
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+ifdef CONFIG_WIRELESS_EXT
+CONFIG_HERMES=m
+CONFIG_HERMES_CACHE_FW_ON_INIT=y
+ifdef CONFIG_PPC_PMAC
+CONFIG_APPLE_AIRPORT=m
+endif #CONFIG_PPC_PMAC
+CONFIG_PLX_HERMES=m
+CONFIG_TMD_HERMES=m
+CONFIG_NORTEL_HERMES=m
+CONFIG_PCI_HERMES=m
+ifdef CONFIG_PCMCIA
+CONFIG_PCMCIA_HERMES=m
+CONFIG_PCMCIA_SPECTRUM=m
+endif #CONFIG_PCMCIA
+endif #CONFIG_WIRELESS_EXT
+
+CONFIG_RTL8192CE=m
+CONFIG_RTL8192SE=m
+CONFIG_RTL8192DE=m
+
+CONFIG_BRCMSMAC=m
+
+CONFIG_MWIFIEX_PCIE=m
+
+endif #CONFIG_PCI
+## end of PCI
+
+ifdef CONFIG_PCMCIA
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS=n
+CONFIG_LIBERTAS_CS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_CS=m
+NEED_LIBERTAS=y
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+endif #CONFIG_PCMCIA
+## end of PCMCIA
+
+# This is required for some cards
+CONFIG_EEPROM_93CX6=m
+
+# USB Drivers
+ifdef CONFIG_USB
+ifndef CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_COMPAT_ZD1211RW=m
+# CONFIG_ZD1211RW_DEBUG=y
+endif #CONFIG_COMPAT_KERNEL_2_6_29
+
+# Sorry, rndis_wlan uses cancel_work_sync which is new and can't be done in compat...
+
+# Wireless RNDIS USB support (RTL8185 802.11g) A-Link WL54PC
+# All of these devices are based on Broadcom 4320 chip which
+# is only wireless RNDIS chip known to date.
+# Note: this depends on CONFIG_USB_NET_RNDIS_HOST and CONFIG_USB_NET_CDCETHER
+# it also requires new RNDIS_HOST and CDC_ETHER modules which we add
+ifdef CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_USB_COMPAT_USBNET=n
+CONFIG_USB_NET_COMPAT_RNDIS_HOST=n
+CONFIG_USB_NET_COMPAT_RNDIS_WLAN=n
+CONFIG_USB_NET_COMPAT_CDCETHER=n
+else #CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_USB_COMPAT_USBNET=m
+ifdef CONFIG_USB_NET_CDCETHER
+CONFIG_USB_NET_COMPAT_RNDIS_HOST=m
+CONFIG_USB_NET_COMPAT_RNDIS_WLAN=m
+endif #CONFIG_USB_NET_CDCETHER
+ifdef CONFIG_USB_NET_CDCETHER_MODULE
+CONFIG_USB_NET_COMPAT_RNDIS_HOST=m
+CONFIG_USB_NET_COMPAT_RNDIS_WLAN=m
+endif #CONFIG_USB_NET_CDCETHER
+CONFIG_USB_NET_COMPAT_CDCETHER=m
+endif #CONFIG_COMPAT_KERNEL_2_6_29
+
+
+CONFIG_P54_USB=m
+CONFIG_RTL8187=m
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_RTL8187_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+
+CONFIG_AT76C50X_USB=m
+
+ifndef CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_CARL9170=m
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_CARL9170_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+# CONFIG_CARL9170_DEBUGFS=y
+CONFIG_CARL9170_WPC=y
+endif #CONFIG_COMPAT_KERNEL_2_6_29
+
+# This activates a threading fix for usb urb.
+# this is mainline commit: b3e670443b7fb8a2d29831b62b44a039c283e351
+# This fix will be included in some stable releases.
+CONFIG_COMPAT_USB_URB_THREAD_FIX=y
+
+CONFIG_ATH9K_HTC=m
+# CONFIG_ATH9K_HTC_DEBUGFS=y
+
+# RT2500USB does not require firmware
+CONFIG_RT2500USB=m
+ifdef CONFIG_CRC_CCITT
+CONFIG_RT2800USB=m
+CONFIG_RT2800USB_RT33XX=y
+CONFIG_RT2800USB_RT35XX=y
+# CONFIG_RT2800USB_RT53XX=y
+CONFIG_RT2800USB_UNKNOWN=y
+endif #CONFIG_CRC_CCITT
+CONFIG_RT2X00_LIB_USB=m
+NEED_RT2X00=y
+# RT73USB requires firmware
+ifdef CONFIG_CRC_ITU_T
+CONFIG_RT73USB=m
+endif #CONFIG_CRC_ITU_T
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_THINFIRM_USB=n
+CONFIG_LIBERTAS_USB=n
+NEED_LIBERTAS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_THINFIRM_USB=m
+CONFIG_LIBERTAS_USB=m
+NEED_LIBERTAS=y
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+CONFIG_ORINOCO_USB=m
+
+CONFIG_BT_HCIBTUSB=m
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_ATH3K=m
+
+CONFIG_RTL8192CU=m
+
+endif #CONFIG_USB end of USB driver list
+
+ifdef CONFIG_SPI_MASTER
+ifndef CONFIG_COMPAT_KERNEL_2_6_25
+
+ifdef CONFIG_CRC7
+CONFIG_WL1251_SPI=m
+CONFIG_WL12XX_SPI=m
+endif #CONFIG_CRC7
+CONFIG_P54_SPI=m
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_SPI=n
+NEED_LIBERTAS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_SPI=m
+NEED_LIBERTAS=y
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+endif #CONFIG_COMPAT_KERNEL_2_6_25
+endif #CONFIG_SPI_MASTER end of SPI driver list
+
+ifdef CONFIG_MMC
+
+CONFIG_SSB_SDIOHOST=y
+CONFIG_B43_SDIO=y
+
+ifdef CONFIG_CRC7
+ifdef CONFIG_WL12XX_PLATFORM_DATA
+CONFIG_COMPAT_WL1251_SDIO=m
+endif #CONFIG_WL12XX_PLATFORM_DATA
+
+ifndef CONFIG_COMPAT_KERNEL_2_6_32
+ifdef CONFIG_WL12XX_PLATFORM_DATA
+CONFIG_COMPAT_WL12XX_SDIO=m
+endif #CONFIG_WL12XX_PLATFORM_DATA
+endif #CONFIG_COMPAT_KERNEL_2_6_32
+
+endif #CONFIG_CRC7
+
+CONFIG_MWIFIEX_SDIO=m
+
+ifndef CONFIG_COMPAT_KERNEL_2_6_32
+CONFIG_COMPAT_LIBERTAS_SDIO=m
+NEED_LIBERTAS=y
+endif #CONFIG_COMPAT_KERNEL_2_6_32
+
+CONFIG_IWM=m
+# CONFIG_IWM_DEBUG=y
+# CONFIG_IWM_TRACING=y
+
+CONFIG_BT_HCIBTSDIO=m
+CONFIG_BT_MRVL_SDIO=m
+
+ifndef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_ATH6KL=m
+CONFIG_ATH6KL_SDIO=m
+CONFIG_ATH6KL_USB=m
+CONFIG_ATH6KL_DEBUG=y
+CONFIG_ATH6KL_REGDOMAIN=y
+CONFIG_SUPPORT_11W=y
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+ifndef CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_BRCMFMAC=m
+endif #CONFIG_COMPAT_KERNEL_2_6_29
+
+endif #CONFIG_MMC
+
+CONFIG_RTLWIFI=m
+CONFIG_RTL8192C_COMMON=m
+
+# Common rt2x00 requirements
+ifeq ($(NEED_RT2X00),y)
+CONFIG_RT2X00=y
+CONFIG_RT2X00_LIB=m
+CONFIG_RT2800_LIB=m
+CONFIG_RT2X00_LIB_FIRMWARE=y
+CONFIG_RT2X00_LIB_CRYPTO=y
+# CONFIG_RT2X00_LIB_SOC=y
+ifdef CONFIG_COMPAT_KERNEL_2_6_25
+CONFIG_RT2X00_LIB_LEDS=n
+else #CONFIG_COMPAT_KERNEL_2_6_25
+ifdef CONFIG_LEDS_CLASS
+CONFIG_RT2X00_LIB_LEDS=y
+endif #CONFIG_LEDS_CLASS
+endif #CONFIG_COMPAT_KERNEL_2_6_25
+# CONFIG_RT2X00_DEBUG=y
+# CONFIG_RT2X00_LIB_DEBUGFS
+endif
+
+# p54
+CONFIG_P54_COMMON=m
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_P54_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+
+# Atheros
+CONFIG_ATH_COMMON=m
+# CONFIG_ATH_DEBUG=y
+
+CONFIG_BRCMUTIL=m
+# CONFIG_BRCMDBG=y
+
+ifdef CONFIG_CRC7
+CONFIG_WL1251=m
+CONFIG_WL12XX=m
+endif #CONFIG_CRC7
+
+CONFIG_MWIFIEX=m
+
+ifndef CONFIG_CORDIC
+CONFIG_COMPAT_CORDIC=y
+endif #CONFIG_CORDIC
+
+ifndef CONFIG_CRC8
+CONFIG_COMPAT_CRC8=y
+endif #CONFIG_CRC8
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+ifeq ($(NEED_LIBERTAS),y)
+CONFIG_LIBERTAS_THINFIRM=m
+CONFIG_LIBERTAS=m
+CONFIG_LIBERTAS_MESH=y
+# CONFIG_LIBERTAS_DEBUG=y
+endif
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+# We need the backported rfkill module on kernel < 2.6.31.
+# In more recent kernel versions use the in kernel rfkill module.
+ifdef CONFIG_COMPAT_KERNEL_2_6_31
+CONFIG_RFKILL_BACKPORT=m
+ifdef CONFIG_LEDS_TRIGGERS
+CONFIG_RFKILL_BACKPORT_LEDS=y
+endif #CONFIG_LEDS_TRIGGERS
+CONFIG_RFKILL_BACKPORT_INPUT=y
+endif #CONFIG_COMPAT_KERNEL_2_6_31
+
diff --git a/crap/0002-ath9k-Add-pktlog-support.patch b/crap/0002-ath9k-Add-pktlog-support.patch
new file mode 100644
index 0000000..67cf684
--- /dev/null
+++ b/crap/0002-ath9k-Add-pktlog-support.patch
@@ -0,0 +1,1411 @@
+Reason for not yet publishing: This code needs more testing and
+enhancements.
+
+From 067eeff8bf0ddb90ea77bf088f924c2a165a98d1 Mon Sep 17 00:00:00 2001
+From: Vasanthakumar Thiagarajan <vasanth@atheros.com>
+Date: Wed, 14 Apr 2010 11:36:44 -0700
+Subject: [PATCH 2/3] ath9k: Add pktlog support
+
+This adds packet log support for all of the supported
+Atheros hardware families under ath9k, AR5008, AR9001, AR9002
+and AR9003. Packet log is used to extract specific descriptor
+and rate control data into a binary file parsed for analysis
+by our systems team.
+
+Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
+---
+ drivers/net/wireless/ath/ath9k/Kconfig | 8 +
+ drivers/net/wireless/ath/ath9k/Makefile | 1 +
+ drivers/net/wireless/ath/ath9k/ar9002_mac.c | 3 +-
+ drivers/net/wireless/ath/ath9k/ar9003_mac.c | 4 +-
+ drivers/net/wireless/ath/ath9k/ath9k.h | 6 +
+ drivers/net/wireless/ath/ath9k/debug.c | 4 +
+ drivers/net/wireless/ath/ath9k/hw-ops.h | 5 +-
+ drivers/net/wireless/ath/ath9k/hw.c | 2 +-
+ drivers/net/wireless/ath/ath9k/hw.h | 4 +-
+ drivers/net/wireless/ath/ath9k/pktlog.c | 783 +++++++++++++++++++++++++++
+ drivers/net/wireless/ath/ath9k/pktlog.h | 242 +++++++++
+ drivers/net/wireless/ath/ath9k/rc.c | 22 +-
+ drivers/net/wireless/ath/ath9k/recv.c | 15 +-
+ drivers/net/wireless/ath/ath9k/xmit.c | 24 +-
+ 14 files changed, 1103 insertions(+), 20 deletions(-)
+ create mode 100644 drivers/net/wireless/ath/ath9k/pktlog.c
+ create mode 100644 drivers/net/wireless/ath/ath9k/pktlog.h
+
+--- a/drivers/net/wireless/ath/ath9k/Kconfig
++++ b/drivers/net/wireless/ath/ath9k/Kconfig
+@@ -40,6 +40,13 @@ config ATH9K_RATE_CONTROL
+ Say Y, if you want to use the ath9k specific rate control
+ module instead of minstrel_ht.
+
++config ATH9K_PKTLOG
++ bool "ath9k packet logging support"
++ depends on ATH9K_DEBUGFS
++ ---help---
++ Say Y to dump frame information during tx/rx, rate information
++ and ani state.
++
+ config ATH9K_HTC
+ tristate "Atheros HTC based wireless cards support"
+ depends on USB && MAC80211
+@@ -61,3 +68,4 @@ config ATH9K_HTC_DEBUGFS
+ depends on ATH9K_HTC && DEBUG_FS
+ ---help---
+ Say Y, if you need access to ath9k_htc's statistics.
++
+--- a/drivers/net/wireless/ath/ath9k/Makefile
++++ b/drivers/net/wireless/ath/ath9k/Makefile
+@@ -9,6 +9,7 @@ ath9k-$(CONFIG_ATH9K_RATE_CONTROL) += rc
+ ath9k-$(CONFIG_PCI) += pci.o
+ ath9k-$(CONFIG_ATHEROS_AR71XX) += ahb.o
+ ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
++ath9k-$(CONFIG_ATH9K_PKTLOG) += pktlog.o
+
+ obj-$(CONFIG_ATH9K) += ath9k.o
+
+--- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c
++++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
+@@ -205,7 +205,8 @@ static void ar9002_hw_fill_txdesc(struct
+ }
+
+ static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
+- struct ath_tx_status *ts)
++ struct ath_tx_status *ts,
++ void *txs_desc)
+ {
+ struct ar5416_desc *ads = AR5416DESC(ds);
+ u32 status;
+--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
+@@ -234,7 +234,8 @@ static void ar9003_hw_fill_txdesc(struct
+ }
+
+ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds,
+- struct ath_tx_status *ts)
++ struct ath_tx_status *ts,
++ void *txs_desc)
+ {
+ struct ar9003_txs *ads;
+ u32 status;
+@@ -308,6 +309,7 @@ static int ar9003_hw_proc_txdesc(struct
+ ts->ts_rssi_ext1 = MS(status, AR_TxRSSIAnt11);
+ ts->ts_rssi_ext2 = MS(status, AR_TxRSSIAnt12);
+
++ memcpy(txs_desc, ads, sizeof(*ads));
+ memset(ads, 0, sizeof(*ads));
+
+ return 0;
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -24,6 +24,7 @@
+
+ #include "debug.h"
+ #include "common.h"
++#include "pktlog.h"
+
+ /*
+ * Header for the ath9k.ko driver core *only* -- hw code nor any other driver
+@@ -550,6 +551,7 @@ struct ath_ant_comb {
+ #define SC_OP_BT_SCAN BIT(13)
+ #define SC_OP_ANI_RUN BIT(14)
+ #define SC_OP_ENABLE_APM BIT(15)
++#define SC_OP_PKTLOGGING BIT(16)
+
+ /* Powersave flags */
+ #define PS_WAIT_FOR_BEACON BIT(0)
+@@ -629,6 +631,10 @@ struct ath_softc {
+ struct list_head nodes; /* basically, stations */
+ unsigned int tx_complete_poll_work_seen;
+ #endif
++#ifdef CONFIG_ATH9K_PKTLOG
++ struct ath_pktlog_debugfs pktlog;
++#endif
++ bool is_pkt_logging;
+ struct ath_beacon_config cur_beacon_conf;
+ struct delayed_work tx_complete_work;
+ struct delayed_work hw_pll_work;
+--- a/drivers/net/wireless/ath/ath9k/debug.c
++++ b/drivers/net/wireless/ath/ath9k/debug.c
+@@ -1066,6 +1066,9 @@ static int open_file_regdump(struct inod
+
+ file->private_data = buf;
+
++ if (ath9k_init_pktlog(sc) != 0)
++ return -EINVAL;
++
+ return 0;
+ }
+
+@@ -1148,6 +1151,7 @@ int ath9k_init_debug(struct ath_hw *ah)
+ sc->debug.regidx = 0;
+ return 0;
+ err:
++ ath9k_deinit_pktlog(sc);
+ debugfs_remove_recursive(sc->debug.debugfs_phy);
+ sc->debug.debugfs_phy = NULL;
+ return -ENOMEM;
+--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
++++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
+@@ -67,9 +67,10 @@ static inline void ath9k_hw_filltxdesc(s
+ }
+
+ static inline int ath9k_hw_txprocdesc(struct ath_hw *ah, void *ds,
+- struct ath_tx_status *ts)
++ struct ath_tx_status *ts,
++ void *txs_desc)
+ {
+- return ath9k_hw_ops(ah)->proc_txdesc(ah, ds, ts);
++ return ath9k_hw_ops(ah)->proc_txdesc(ah, ds, ts, txs_desc);
+ }
+
+ static inline void ath9k_hw_set11n_txdesc(struct ath_hw *ah, void *ds,
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -2192,7 +2192,7 @@ void ath9k_hw_setrxfilter(struct ath_hw
+ phybits |= AR_PHY_ERR_RADAR;
+ if (bits & ATH9K_RX_FILTER_PHYERR)
+ phybits |= AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING;
+- REG_WRITE(ah, AR_PHY_ERR, phybits);
++ REG_WRITE(ah, AR_PHY_ERR, 0xffffffff);
+
+ if (phybits)
+ REG_WRITE(ah, AR_RXCFG,
+--- a/drivers/net/wireless/ath/ath9k/hw.h
++++ b/drivers/net/wireless/ath/ath9k/hw.h
+@@ -620,7 +620,7 @@ struct ath_hw_ops {
+ const void *ds0, dma_addr_t buf_addr,
+ unsigned int qcu);
+ int (*proc_txdesc)(struct ath_hw *ah, void *ds,
+- struct ath_tx_status *ts);
++ struct ath_tx_status *ts, void* txs_desc);
+ void (*set11n_txdesc)(struct ath_hw *ah, void *ds,
+ u32 pktLen, enum ath9k_pkt_type type,
+ u32 txPower, u32 keyIx,
+@@ -856,6 +856,8 @@ struct ath_hw {
+
+ /* Enterprise mode cap */
+ u32 ent_mode;
++
++ bool is_pkt_logging;
+ };
+
+ static inline struct ath_common *ath9k_hw_common(struct ath_hw *ah)
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath9k/pktlog.c
+@@ -0,0 +1,783 @@
++
++#include <linux/vmalloc.h>
++#include <linux/highmem.h>
++#include "ath9k.h"
++
++static int ath9k_debugfs_open(struct inode *inode, struct file *file)
++{
++ file->private_data = inode->i_private;
++ return 0;
++}
++
++static struct page *pktlog_virt_to_logical(void *addr)
++{
++ struct page *page;
++ unsigned long vpage = 0UL;
++
++ page = vmalloc_to_page(addr);
++ if (page) {
++ vpage = (unsigned long) page_address(page);
++ vpage |= ((unsigned long) addr & (PAGE_SIZE - 1));
++ }
++ return virt_to_page((void *) vpage);
++}
++
++static void ath_pktlog_release(struct ath_pktlog *pktlog)
++{
++ unsigned long page_cnt, vaddr;
++ struct page *page;
++
++ page_cnt =
++ ((sizeof(*(pktlog->pktlog_buf)) +
++ pktlog->pktlog_buf_size) / PAGE_SIZE) + 1;
++
++ for (vaddr = (unsigned long) (pktlog->pktlog_buf); vaddr <
++ (unsigned long) (pktlog->pktlog_buf) +
++ (page_cnt * PAGE_SIZE);
++ vaddr += PAGE_SIZE) {
++ page = pktlog_virt_to_logical((void *) vaddr);
++ clear_bit(PG_reserved, &page->flags);
++ }
++
++ vfree(pktlog->pktlog_buf);
++ pktlog->pktlog_buf = NULL;
++}
++
++static int ath_alloc_pktlog_buf(struct ath_softc *sc)
++{
++ u32 page_cnt;
++ unsigned long vaddr;
++ struct page *page;
++ struct ath_pktlog *pktlog = &sc->pktlog.pktlog;
++
++ if (pktlog->pktlog_buf_size == 0)
++ return -EINVAL;
++
++ page_cnt = (sizeof(*(pktlog->pktlog_buf)) +
++ pktlog->pktlog_buf_size) / PAGE_SIZE;
++
++ pktlog->pktlog_buf = vmalloc((page_cnt + 2) * PAGE_SIZE);
++ if (pktlog->pktlog_buf == NULL) {
++ printk(KERN_ERR "Failed to allocate memory for pktlog");
++ return -ENOMEM;
++ }
++
++ pktlog->pktlog_buf = (struct ath_pktlog_buf *)
++ (((unsigned long)
++ (pktlog->pktlog_buf)
++ + PAGE_SIZE - 1) & PAGE_MASK);
++
++ for (vaddr = (unsigned long) (pktlog->pktlog_buf);
++ vaddr < ((unsigned long) (pktlog->pktlog_buf)
++ + (page_cnt * PAGE_SIZE)); vaddr += PAGE_SIZE) {
++ page = pktlog_virt_to_logical((void *)vaddr);
++ set_bit(PG_reserved, &page->flags);
++ }
++
++ return 0;
++}
++
++static void ath_init_pktlog_buf(struct ath_pktlog *pktlog)
++{
++ pktlog->pktlog_buf->bufhdr.magic_num = PKTLOG_MAGIC_NUM;
++ pktlog->pktlog_buf->bufhdr.version = CUR_PKTLOG_VER;
++ pktlog->pktlog_buf->rd_offset = -1;
++ pktlog->pktlog_buf->wr_offset = 0;
++ if (pktlog->pktlog_filter == 0)
++ pktlog->pktlog_filter = ATH_PKTLOG_FILTER_DEFAULT;
++}
++
++static char *ath_pktlog_getbuf(struct ath_pktlog *pl_info,
++ u16 log_type, size_t log_size,
++ u32 flags)
++{
++ struct ath_pktlog_buf *log_buf;
++ struct ath_pktlog_hdr *log_hdr;
++ int32_t cur_wr_offset, buf_size;
++ char *log_ptr;
++
++ log_buf = pl_info->pktlog_buf;
++ buf_size = pl_info->pktlog_buf_size;
++
++ spin_lock_bh(&pl_info->pktlog_lock);
++ cur_wr_offset = log_buf->wr_offset;
++ /* Move read offset to the next entry if there is a buffer overlap */
++ if (log_buf->rd_offset >= 0) {
++ if ((cur_wr_offset <= log_buf->rd_offset)
++ && (cur_wr_offset +
++ sizeof(struct ath_pktlog_hdr)) >
++ log_buf->rd_offset)
++ PKTLOG_MOV_RD_IDX(log_buf->rd_offset, log_buf,
++ buf_size);
++ } else {
++ log_buf->rd_offset = cur_wr_offset;
++ }
++
++ log_hdr =
++ (struct ath_pktlog_hdr *) (log_buf->log_data + cur_wr_offset);
++ log_hdr->log_type = log_type;
++ log_hdr->flags = flags;
++ log_hdr->timestamp = jiffies;
++ log_hdr->size = (u16) log_size;
++
++ cur_wr_offset += sizeof(*log_hdr);
++
++ if ((buf_size - cur_wr_offset) < log_size) {
++ while ((cur_wr_offset <= log_buf->rd_offset)
++ && (log_buf->rd_offset < buf_size))
++ PKTLOG_MOV_RD_IDX(log_buf->rd_offset, log_buf,
++ buf_size);
++ cur_wr_offset = 0;
++ }
++
++ while ((cur_wr_offset <= log_buf->rd_offset)
++ && (cur_wr_offset + log_size) > log_buf->rd_offset)
++ PKTLOG_MOV_RD_IDX(log_buf->rd_offset, log_buf, buf_size);
++
++ log_ptr = &(log_buf->log_data[cur_wr_offset]);
++
++ cur_wr_offset += log_hdr->size;
++
++ log_buf->wr_offset =
++ ((buf_size - cur_wr_offset) >=
++ sizeof(struct ath_pktlog_hdr)) ? cur_wr_offset : 0;
++ spin_unlock_bh(&pl_info->pktlog_lock);
++
++ return log_ptr;
++}
++
++static void ath9k_hw_get_descinfo(struct ath_hw *ah, struct ath_desc_info *desc_info)
++{
++ desc_info->txctl_numwords = TXCTL_NUMWORDS(ah);
++ desc_info->txctl_offset = TXCTL_OFFSET(ah);
++ desc_info->txstatus_numwords = TXSTATUS_NUMWORDS(ah);
++ desc_info->txstatus_offset = TXSTATUS_OFFSET(ah);
++
++ desc_info->rxctl_numwords = RXCTL_NUMWORDS(ah);
++ desc_info->rxctl_offset = RXCTL_OFFSET(ah);
++ desc_info->rxstatus_numwords = RXSTATUS_NUMWORDS(ah);
++ desc_info->rxstatus_offset = RXSTATUS_OFFSET(ah);
++}
++
++static int pktlog_pgfault(struct vm_area_struct *vma, struct vm_fault *vmf)
++{
++ unsigned long address = (unsigned long) vmf->virtual_address;
++
++ if (address == 0UL)
++ return VM_FAULT_NOPAGE;
++
++ if (vmf->pgoff > vma->vm_end)
++ return VM_FAULT_SIGBUS;
++
++ get_page(virt_to_page(address));
++ vmf->page = virt_to_page(address);
++ return VM_FAULT_MINOR;
++}
++
++static struct vm_operations_struct pktlog_vmops = {
++ .fault = pktlog_pgfault
++};
++
++static int ath_pktlog_mmap(struct file *file, struct vm_area_struct *vma)
++{
++ struct ath_softc *sc = file->private_data;
++
++ /* entire buffer should be mapped */
++ if (vma->vm_pgoff != 0)
++ return -EINVAL;
++
++ if (!sc->pktlog.pktlog.pktlog_buf) {
++ printk(KERN_ERR "Can't allocate pktlog buf");
++ return -ENOMEM;
++ }
++
++ vma->vm_flags |= VM_LOCKED;
++ vma->vm_ops = &pktlog_vmops;
++
++ return 0;
++}
++
++static ssize_t ath_pktlog_read(struct file *file, char __user *userbuf,
++ size_t count, loff_t *ppos)
++{
++ size_t bufhdr_size;
++ size_t nbytes = 0, ret_val = 0;
++ int rem_len;
++ int start_offset, end_offset;
++ int fold_offset, ppos_data, cur_rd_offset;
++ struct ath_softc *sc = file->private_data;
++ struct ath_pktlog *pktlog_info = &sc->pktlog.pktlog;
++ struct ath_pktlog_buf *log_buf = pktlog_info->pktlog_buf;
++
++ if (log_buf == NULL)
++ return 0;
++
++ bufhdr_size = sizeof(log_buf->bufhdr);
++
++ /* copy valid log entries from circular buffer into user space */
++ rem_len = count;
++
++ nbytes = 0;
++
++ if (*ppos < bufhdr_size) {
++ nbytes = min((int) (bufhdr_size - *ppos), rem_len);
++ if (copy_to_user(userbuf,
++ ((char *) &log_buf->bufhdr) + *ppos, nbytes))
++ return -EFAULT;
++ rem_len -= nbytes;
++ ret_val += nbytes;
++ }
++
++ start_offset = log_buf->rd_offset;
++
++ if ((rem_len == 0) || (start_offset < 0))
++ goto read_done;
++
++ fold_offset = -1;
++ cur_rd_offset = start_offset;
++
++ /* Find the last offset and fold-offset if the buffer is folded */
++ do {
++ struct ath_pktlog_hdr *log_hdr;
++ int log_data_offset;
++
++ log_hdr =
++ (struct ath_pktlog_hdr *) (log_buf->log_data +
++ cur_rd_offset);
++
++ log_data_offset = cur_rd_offset + sizeof(struct ath_pktlog_hdr);
++
++ if ((fold_offset == -1)
++ && ((pktlog_info->pktlog_buf_size -
++ log_data_offset) <= log_hdr->size))
++ fold_offset = log_data_offset - 1;
++
++ PKTLOG_MOV_RD_IDX(cur_rd_offset, log_buf,
++ pktlog_info->pktlog_buf_size);
++
++ if ((fold_offset == -1) && (cur_rd_offset == 0)
++ && (cur_rd_offset != log_buf->wr_offset))
++ fold_offset = log_data_offset + log_hdr->size - 1;
++
++ end_offset = log_data_offset + log_hdr->size - 1;
++ } while (cur_rd_offset != log_buf->wr_offset);
++
++ ppos_data = *ppos + ret_val - bufhdr_size + start_offset;
++
++ if (fold_offset == -1) {
++ if (ppos_data > end_offset)
++ goto read_done;
++
++ nbytes = min(rem_len, end_offset - ppos_data + 1);
++ if (copy_to_user(userbuf + ret_val,
++ log_buf->log_data + ppos_data, nbytes))
++ return -EFAULT;
++ ret_val += nbytes;
++ rem_len -= nbytes;
++ } else {
++ if (ppos_data <= fold_offset) {
++ nbytes = min(rem_len, fold_offset - ppos_data + 1);
++ if (copy_to_user(userbuf + ret_val,
++ log_buf->log_data + ppos_data,
++ nbytes))
++ return -EFAULT;
++ ret_val += nbytes;
++ rem_len -= nbytes;
++ }
++
++ if (rem_len == 0)
++ goto read_done;
++
++ ppos_data =
++ *ppos + ret_val - (bufhdr_size +
++ (fold_offset - start_offset + 1));
++
++ if (ppos_data <= end_offset) {
++ nbytes = min(rem_len, end_offset - ppos_data + 1);
++ if (copy_to_user(userbuf + ret_val, log_buf->log_data
++ + ppos_data,
++ nbytes))
++ return -EFAULT;
++ ret_val += nbytes;
++ rem_len -= nbytes;
++ }
++ }
++
++read_done:
++ *ppos += ret_val;
++
++ return ret_val;
++}
++
++static const struct file_operations fops_pktlog_dump = {
++ .read = ath_pktlog_read,
++ .mmap = ath_pktlog_mmap,
++ .open = ath9k_debugfs_open
++};
++
++static ssize_t write_pktlog_start(struct file *file, const char __user *ubuf,
++ size_t count, loff_t *ppos)
++{
++ struct ath_softc *sc = file->private_data;
++ struct ath_pktlog *pktlog = &sc->pktlog.pktlog;
++ char buf[32];
++ int buf_size;
++ int start_pktlog, err;
++
++ buf_size = min(count, sizeof(buf) - 1);
++ if (copy_from_user(buf, ubuf, buf_size))
++ return -EFAULT;
++
++ sscanf(buf, "%d", &start_pktlog);
++ if (start_pktlog) {
++ if (pktlog->pktlog_buf != NULL)
++ ath_pktlog_release(pktlog);
++
++ err = ath_alloc_pktlog_buf(sc);
++ if (err != 0)
++ return err;
++
++ ath_init_pktlog_buf(pktlog);
++ pktlog->pktlog_buf->rd_offset = -1;
++ pktlog->pktlog_buf->wr_offset = 0;
++ sc->is_pkt_logging = 1;
++ } else {
++ sc->is_pkt_logging = 0;
++ }
++
++ sc->sc_ah->is_pkt_logging = sc->is_pkt_logging;
++ return count;
++}
++
++static ssize_t read_pktlog_start(struct file *file, char __user *ubuf,
++ size_t count, loff_t *ppos)
++{
++ char buf[32];
++ struct ath_softc *sc = file->private_data;
++ int len = 0;
++
++ len = scnprintf(buf, sizeof(buf) - len, "%d", sc->is_pkt_logging);
++ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
++}
++
++static const struct file_operations fops_pktlog_start = {
++ .read = read_pktlog_start,
++ .write = write_pktlog_start,
++ .open = ath9k_debugfs_open
++};
++
++static ssize_t pktlog_size_write(struct file *file, const char __user *ubuf,
++ size_t count, loff_t *ppos)
++{
++ struct ath_softc *sc = file->private_data;
++ char buf[32];
++ u32 pktlog_size;
++ int buf_size;
++
++ buf_size = min(count, sizeof(buf) - 1);
++ if (copy_from_user(buf, ubuf, buf_size))
++ return -EFAULT;
++
++ sscanf(buf, "%d", &pktlog_size);
++
++ if (pktlog_size == sc->pktlog.pktlog.pktlog_buf_size)
++ return count;
++
++ if (sc->is_pkt_logging) {
++ printk(KERN_DEBUG "Stop packet logging before"
++ " changing the pktlog size \n");
++ return -EINVAL;
++ }
++
++ sc->pktlog.pktlog.pktlog_buf_size = pktlog_size;
++
++ return count;
++}
++
++static ssize_t pktlog_size_read(struct file *file, char __user *ubuf,
++ size_t count, loff_t *ppos)
++{
++ char buf[32];
++ struct ath_softc *sc = file->private_data;
++ int len = 0;
++
++ len = scnprintf(buf, sizeof(buf) - len, "%ul",
++ sc->pktlog.pktlog.pktlog_buf_size);
++ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
++}
++
++static const struct file_operations fops_pktlog_size = {
++ .read = pktlog_size_read,
++ .write = pktlog_size_write,
++ .open = ath9k_debugfs_open
++};
++
++static ssize_t pktlog_filter_write(struct file *file, const char __user *ubuf,
++ size_t count, loff_t *ppos)
++{
++ char buf[32];
++ struct ath_softc *sc = file->private_data;
++ u32 filter;
++ int buf_count;
++
++ buf_count = min(count, sizeof(buf) - 1);
++ if (copy_from_user(buf, ubuf, buf_count))
++ return -EFAULT;
++
++ if (sscanf(buf, "%x", &filter))
++ sc->pktlog.pktlog.pktlog_filter = filter;
++ else
++ sc->pktlog.pktlog.pktlog_filter = 0;
++
++ return count;
++}
++
++static ssize_t pktlog_filter_read(struct file *file, char __user *ubuf,
++ size_t count, loff_t *ppos)
++{
++ char buf[32];
++ struct ath_softc *sc = file->private_data;
++ int len = 0;
++
++ len = scnprintf(buf, sizeof(buf) - len, "%ul",
++ sc->pktlog.pktlog.pktlog_filter);
++
++ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
++}
++
++static const struct file_operations fops_pktlog_filter = {
++ .read = pktlog_filter_read,
++ .write = pktlog_filter_write,
++ .open = ath9k_debugfs_open
++};
++
++void ath_pktlog_txctl(struct ath_softc *sc, struct ath_buf *bf)
++{
++ struct ath_pktlog_txctl *tx_log;
++ struct ath_pktlog *pl_info;
++ struct ieee80211_hdr *hdr;
++ struct ath_desc_info desc_info;
++ int i;
++ u32 *ds_words, flags = 0;
++
++ pl_info = &sc->pktlog.pktlog;
++
++ if ((pl_info->pktlog_filter & ATH_PKTLOG_TX) == 0 ||
++ bf->bf_mpdu == NULL || !sc->is_pkt_logging)
++ return;
++
++ flags |= (((sc->sc_ah->hw_version.macRev <<
++ PHFLAGS_MACREV_SFT) & PHFLAGS_MACREV_MASK) |
++ ((sc->sc_ah->hw_version.macVersion << PHFLAGS_MACVERSION_SFT)
++ & PHFLAGS_MACVERSION_MASK));
++
++ tx_log = (struct ath_pktlog_txctl *)ath_pktlog_getbuf(pl_info,
++ PKTLOG_TYPE_TXCTL, sizeof(*tx_log), flags);
++
++ memset(tx_log, 0, sizeof(*tx_log));
++
++ hdr = (struct ieee80211_hdr *) bf->bf_mpdu->data;
++ tx_log->framectrl = hdr->frame_control;
++ tx_log->seqctrl = hdr->seq_ctrl;
++
++ if (ieee80211_has_tods(tx_log->framectrl)) {
++ tx_log->bssid_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
++ (hdr->addr1[ETH_ALEN - 1]);
++ tx_log->sa_tail = (hdr->addr2[ETH_ALEN - 2] << 8) |
++ (hdr->addr2[ETH_ALEN - 1]);
++ tx_log->da_tail = (hdr->addr3[ETH_ALEN - 2] << 8) |
++ (hdr->addr3[ETH_ALEN - 1]);
++ } else if (ieee80211_has_fromds(tx_log->framectrl)) {
++ tx_log->bssid_tail = (hdr->addr2[ETH_ALEN - 2] << 8) |
++ (hdr->addr2[ETH_ALEN - 1]);
++ tx_log->sa_tail = (hdr->addr3[ETH_ALEN - 2] << 8) |
++ (hdr->addr3[ETH_ALEN - 1]);
++ tx_log->da_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
++ (hdr->addr1[ETH_ALEN - 1]);
++ } else {
++ tx_log->bssid_tail = (hdr->addr3[ETH_ALEN - 2] << 8) |
++ (hdr->addr3[ETH_ALEN - 1]);
++ tx_log->sa_tail = (hdr->addr2[ETH_ALEN - 2] << 8) |
++ (hdr->addr2[ETH_ALEN - 1]);
++ tx_log->da_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
++ (hdr->addr1[ETH_ALEN - 1]);
++ }
++
++ ath9k_hw_get_descinfo(sc->sc_ah, &desc_info);
++
++ ds_words = (u32 *)(bf->bf_desc) + desc_info.txctl_offset;
++ for (i = 0; i < desc_info.txctl_numwords; i++)
++ tx_log->txdesc_ctl[i] = ds_words[i];
++}
++
++void ath_pktlog_txstatus(struct ath_softc *sc, void *ds)
++{
++ struct ath_pktlog_txstatus *tx_log;
++ struct ath_pktlog *pl_info;
++ struct ath_desc_info desc_info;
++ int i;
++ u32 *ds_words, flags = 0;
++
++ pl_info = &sc->pktlog.pktlog;
++
++ if ((pl_info->pktlog_filter & ATH_PKTLOG_TX) == 0 ||
++ !sc->is_pkt_logging)
++ return;
++
++ flags |= (((sc->sc_ah->hw_version.macRev <<
++ PHFLAGS_MACREV_SFT) & PHFLAGS_MACREV_MASK) |
++ ((sc->sc_ah->hw_version.macVersion << PHFLAGS_MACVERSION_SFT)
++ & PHFLAGS_MACVERSION_MASK));
++ tx_log = (struct ath_pktlog_txstatus *)ath_pktlog_getbuf(pl_info,
++ PKTLOG_TYPE_TXSTATUS, sizeof(*tx_log), flags);
++
++ memset(tx_log, 0, sizeof(*tx_log));
++
++ ath9k_hw_get_descinfo(sc->sc_ah, &desc_info);
++
++ ds_words = (u32 *)(ds) + desc_info.txstatus_offset;
++
++ for (i = 0; i < desc_info.txstatus_numwords; i++)
++ tx_log->txdesc_status[i] = ds_words[i];
++}
++
++void ath_pktlog_rx(struct ath_softc *sc, void *desc, struct sk_buff *skb)
++{
++ struct ath_pktlog_rx *rx_log;
++ struct ath_pktlog *pl_info;
++ struct ieee80211_hdr *hdr;
++ struct ath_desc_info desc_info;
++ int i;
++ u32 *ds_words, flags = 0;
++
++ pl_info = &sc->pktlog.pktlog;
++
++ if ((pl_info->pktlog_filter & ATH_PKTLOG_RX) == 0 ||
++ !sc->is_pkt_logging)
++ return;
++
++ flags |= (((sc->sc_ah->hw_version.macRev <<
++ PHFLAGS_MACREV_SFT) & PHFLAGS_MACREV_MASK) |
++ ((sc->sc_ah->hw_version.macVersion <<
++ PHFLAGS_MACVERSION_SFT) & PHFLAGS_MACVERSION_MASK));
++
++ rx_log = (struct ath_pktlog_rx *)ath_pktlog_getbuf(pl_info, PKTLOG_TYPE_RX,
++ sizeof(*rx_log), flags);
++
++ memset(rx_log, 0, sizeof(*rx_log));
++
++ if (skb->len > sizeof(struct ieee80211_hdr)) {
++ hdr = (struct ieee80211_hdr *) skb->data;
++ rx_log->framectrl = hdr->frame_control;
++ rx_log->seqctrl = hdr->seq_ctrl;
++
++ if (ieee80211_has_tods(rx_log->framectrl)) {
++ rx_log->bssid_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
++ (hdr->addr1[ETH_ALEN - 1]);
++ rx_log->sa_tail = (hdr->addr2[ETH_ALEN - 2] << 8) |
++ (hdr->addr2[ETH_ALEN - 1]);
++ rx_log->da_tail = (hdr->addr3[ETH_ALEN - 2] << 8) |
++ (hdr->addr3[ETH_ALEN - 1]);
++ } else if (ieee80211_has_fromds(rx_log->framectrl)) {
++ rx_log->bssid_tail = (hdr->addr2[ETH_ALEN - 2] << 8) |
++ (hdr->addr2[ETH_ALEN - 1]);
++ rx_log->sa_tail = (hdr->addr3[ETH_ALEN - 2] << 8) |
++ (hdr->addr3[ETH_ALEN - 1]);
++ rx_log->da_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
++ (hdr->addr1[ETH_ALEN - 1]);
++ } else {
++ rx_log->bssid_tail = (hdr->addr3[ETH_ALEN - 2] << 8) |
++ (hdr->addr3[ETH_ALEN - 1]);
++ rx_log->sa_tail = (hdr->addr2[ETH_ALEN - 2] << 8) |
++ (hdr->addr2[ETH_ALEN - 1]);
++ rx_log->da_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
++ (hdr->addr1[ETH_ALEN - 1]);
++ }
++ } else {
++ hdr = (struct ieee80211_hdr *) skb->data;
++
++ if (ieee80211_is_ctl(hdr->frame_control)) {
++ rx_log->framectrl = hdr->frame_control;
++ rx_log->da_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
++ (hdr->addr1[ETH_ALEN - 1]);
++ if (skb->len < sizeof(struct ieee80211_rts)) {
++ rx_log->sa_tail = 0;
++ } else {
++ rx_log->sa_tail = (hdr->addr2[ETH_ALEN - 2]
++ << 8) |
++ (hdr->addr2[ETH_ALEN - 1]);
++ }
++ } else {
++ rx_log->framectrl = 0xFFFF;
++ rx_log->da_tail = 0;
++ rx_log->sa_tail = 0;
++ }
++
++ rx_log->seqctrl = 0;
++ rx_log->bssid_tail = 0;
++ }
++
++ ath9k_hw_get_descinfo(sc->sc_ah, &desc_info);
++
++ ds_words = (u32 *)(desc) + desc_info.rxstatus_offset;
++
++ for (i = 0; i < desc_info.rxstatus_numwords; i++)
++ rx_log->rxdesc_status[i] = ds_words[i];
++}
++
++void ath9k_pktlog_rc(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv,
++ int8_t ratecode, u8 rate, int8_t is_probing, u16 ac)
++{
++ struct ath_pktlog_rcfind *rcf_log;
++ struct ath_pktlog *pl_info;
++ u32 flags = 0;
++
++ pl_info = &sc->pktlog.pktlog;
++
++ if ((pl_info->pktlog_filter & ATH_PKTLOG_RCFIND) == 0 ||
++ !sc->is_pkt_logging)
++ return;
++
++ flags |= (((sc->sc_ah->hw_version.macRev <<
++ PHFLAGS_MACREV_SFT) & PHFLAGS_MACREV_MASK) |
++ ((sc->sc_ah->hw_version.macVersion <<
++ PHFLAGS_MACVERSION_SFT) & PHFLAGS_MACVERSION_MASK));
++ rcf_log = (struct ath_pktlog_rcfind *)ath_pktlog_getbuf(pl_info,
++ PKTLOG_TYPE_RCFIND, sizeof(*rcf_log), flags);
++
++ rcf_log->rate = rate;
++ rcf_log->rateCode = ratecode;
++ rcf_log->rcProbeRate = is_probing ? ath_rc_priv->probe_rate : 0;
++ rcf_log->isProbing = is_probing;
++ rcf_log->ac = ac;
++ rcf_log->rcRateMax = ath_rc_priv->rate_max_phy;
++ rcf_log->rcRateTableSize = ath_rc_priv->rate_table_size;
++}
++
++void ath9k_pktlog_rcupdate(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv, u8 tx_rate,
++ u8 rate_code, u8 xretries, u8 retries, int8_t rssi, u16 ac)
++{
++ struct ath_pktlog_rcupdate *rcu_log;
++ struct ath_pktlog *pl_info;
++ int i;
++ u32 flags = 0;
++
++ pl_info = &sc->pktlog.pktlog;
++
++ if ((pl_info->pktlog_filter & ATH_PKTLOG_RCUPDATE) == 0 ||
++ !sc->is_pkt_logging)
++ return;
++
++ flags |= (((sc->sc_ah->hw_version.macRev <<
++ PHFLAGS_MACREV_SFT) & PHFLAGS_MACREV_MASK) |
++ ((sc->sc_ah->hw_version.macVersion <<
++ PHFLAGS_MACVERSION_SFT) & PHFLAGS_MACVERSION_MASK));
++ rcu_log = (struct ath_pktlog_rcupdate *)ath_pktlog_getbuf(pl_info,
++ PKTLOG_TYPE_RCUPDATE,
++ sizeof(*rcu_log), flags);
++
++ memset(rcu_log, 0, sizeof(*rcu_log));
++
++ rcu_log->txRate = tx_rate;
++ rcu_log->rateCode = rate_code;
++ rcu_log->Xretries = xretries;
++ rcu_log->retries = retries;
++ rcu_log->rssiAck = rssi;
++ rcu_log->ac = ac;
++ rcu_log->rcProbeRate = ath_rc_priv->probe_rate;
++ rcu_log->rcRateMax = ath_rc_priv->rate_max_phy;
++
++ for (i = 0; i < RATE_TABLE_SIZE; i++) {
++ rcu_log->rcPer[i] = ath_rc_priv->per[i];
++ }
++}
++
++void ath9k_pktlog_txcomplete(struct ath_softc *sc, struct list_head *bf_head,
++ struct ath_buf *bf, struct ath_buf *bf_last)
++{
++ struct log_tx ;
++ struct ath_buf *tbf;
++
++ list_for_each_entry(tbf, bf_head, list)
++ ath_pktlog_txctl(sc, tbf);
++
++ if (bf->bf_next == NULL && bf_last->bf_stale)
++ ath_pktlog_txctl(sc, bf_last);
++}
++
++void ath9k_pktlog_txctrl(struct ath_softc *sc, struct list_head *bf_head, struct ath_buf *lastbf)
++{
++ struct log_tx ;
++ struct ath_buf *tbf;
++
++ list_for_each_entry(tbf, bf_head, list)
++ ath_pktlog_txctl(sc, tbf);
++
++ /* log the last descriptor. */
++ ath_pktlog_txctl(sc, lastbf);
++}
++
++static void pktlog_init(struct ath_softc *sc)
++{
++ spin_lock_init(&sc->pktlog.pktlog.pktlog_lock);
++ sc->pktlog.pktlog.pktlog_buf_size = ATH_DEBUGFS_PKTLOG_SIZE_DEFAULT;
++ sc->pktlog.pktlog.pktlog_buf = NULL;
++ sc->pktlog.pktlog.pktlog_filter = 0;
++}
++
++int ath9k_init_pktlog(struct ath_softc *sc)
++{
++ sc->pktlog.debugfs_pktlog = debugfs_create_dir("pktlog",
++ sc->debug.debugfs_phy);
++ if (!sc->pktlog.debugfs_pktlog)
++ goto err;
++
++ sc->pktlog.pktlog_start = debugfs_create_file("pktlog_start",
++ S_IRUGO | S_IWUSR,
++ sc->pktlog.debugfs_pktlog,
++ sc, &fops_pktlog_start);
++ if (!sc->pktlog.pktlog_start)
++ goto err;
++
++ sc->pktlog.pktlog_size = debugfs_create_file("pktlog_size",
++ S_IRUGO | S_IWUSR,
++ sc->pktlog.debugfs_pktlog,
++ sc, &fops_pktlog_size);
++ if (!sc->pktlog.pktlog_size)
++ goto err;
++
++ sc->pktlog.pktlog_filter = debugfs_create_file("pktlog_filter",
++ S_IRUGO | S_IWUSR,
++ sc->pktlog.debugfs_pktlog,
++ sc, &fops_pktlog_filter);
++ if (!sc->pktlog.pktlog_filter)
++ goto err;
++
++ sc->pktlog.pktlog_dump = debugfs_create_file("pktlog_dump",
++ S_IRUGO,
++ sc->pktlog.debugfs_pktlog,
++ sc, &fops_pktlog_dump);
++ if (!sc->pktlog.pktlog_dump)
++ goto err;
++
++ pktlog_init(sc);
++
++ return 0;
++
++err:
++ return -ENOMEM;
++}
++
++void ath9k_deinit_pktlog(struct ath_softc *sc)
++{
++ struct ath_pktlog *pktlog = &sc->pktlog.pktlog;
++
++ if (pktlog->pktlog_buf != NULL)
++ ath_pktlog_release(pktlog);
++
++ debugfs_remove(sc->pktlog.pktlog_start);
++ debugfs_remove(sc->pktlog.pktlog_size);
++ debugfs_remove(sc->pktlog.pktlog_filter);
++ debugfs_remove(sc->pktlog.pktlog_dump);
++ debugfs_remove(sc->pktlog.debugfs_pktlog);
++}
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath9k/pktlog.h
+@@ -0,0 +1,242 @@
++#ifndef PKTLOG_H
++#define PKTLOG_H
++
++#ifdef CONFIG_ATH9K_PKTLOG
++#define CUR_PKTLOG_VER 10010 /* Packet log version */
++#define PKTLOG_MAGIC_NUM 7735225
++#define ATH_PKTLOG_TX 0x000000001
++#define ATH_PKTLOG_RX 0x000000002
++#define ATH_PKTLOG_RCFIND 0x000000004
++#define ATH_PKTLOG_RCUPDATE 0x000000008
++
++#define ATH_DEBUGFS_PKTLOG_SIZE_DEFAULT (1024 * 1024)
++#define ATH_PKTLOG_FILTER_DEFAULT (ATH_PKTLOG_TX | ATH_PKTLOG_RX | \
++ ATH_PKTLOG_RCFIND | ATH_PKTLOG_RCUPDATE)
++
++#define PHFLAGS_MACVERSION_MASK 0x00ff0000
++#define PHFLAGS_MACVERSION_SFT 16
++#define PHFLAGS_MACREV_MASK 0xff0 /* MAC revision */
++#define PHFLAGS_MACREV_SFT 4
++
++struct ath_pktlog_hdr {
++ u32 flags;
++ u16 log_type; /* Type of log information foll this header */
++ int16_t size; /* Size of variable length log information in bytes */
++ u32 timestamp;
++} __packed;
++
++/* Types of packet log events */
++#define PKTLOG_TYPE_TXCTL 0
++#define PKTLOG_TYPE_TXSTATUS 1
++#define PKTLOG_TYPE_RX 2
++#define PKTLOG_TYPE_RCFIND 3
++#define PKTLOG_TYPE_RCUPDATE 4
++
++#define PKTLOG_MAX_TXCTL_WORDS 12
++#define PKTLOG_MAX_TXSTATUS_WORDS 10
++#define PKTLOG_MAX_PROTO_WORDS 16
++
++struct ath_pktlog_txctl {
++ __le16 framectrl; /* frame control field from header */
++ __le16 seqctrl; /* frame control field from header */
++ u16 bssid_tail; /* last two octets of bssid */
++ u16 sa_tail; /* last two octets of SA */
++ u16 da_tail; /* last two octets of DA */
++ u16 resvd;
++ u32 txdesc_ctl[PKTLOG_MAX_TXCTL_WORDS]; /* Tx descriptor words */
++ unsigned long proto_hdr; /* protocol header (variable length!) */
++ int32_t misc[0]; /* Can be used for HT specific or other misc info */
++} __packed;
++
++struct ath_pktlog_txstatus {
++ /* Tx descriptor status words */
++ u32 txdesc_status[PKTLOG_MAX_TXSTATUS_WORDS];
++ int32_t misc[0]; /* Can be used for HT specific or other misc info */
++} __packed;
++
++#define PKTLOG_MAX_RXSTATUS_WORDS 11
++
++struct ath_pktlog_rx {
++ u16 framectrl; /* frame control field from header */
++ u16 seqctrl; /* sequence control field */
++ u16 bssid_tail; /* last two octets of bssid */
++ u16 sa_tail; /* last two octets of SA */
++ u16 da_tail; /* last two octets of DA */
++ u16 resvd;
++ u32 rxdesc_status[PKTLOG_MAX_RXSTATUS_WORDS]; /* Rx descriptor words */
++ unsigned long proto_hdr; /* protocol header (variable length!) */
++ int32_t misc[0]; /* Can be used for HT specific or other misc info */
++} __packed;
++
++struct ath_pktlog_rcfind {
++ u8 rate;
++ u8 rateCode;
++ s8 rcRssiLast;
++ s8 rcRssiLastPrev;
++ s8 rcRssiLastPrev2;
++ s8 rssiReduce;
++ u8 rcProbeRate;
++ s8 isProbing;
++ s8 primeInUse;
++ s8 currentPrimeState;
++ u8 rcRateTableSize;
++ u8 rcRateMax;
++ u8 ac;
++ int32_t misc[0]; /* Can be used for HT specific or other misc info */
++} __packed;
++
++struct ath_pktlog_rcupdate {
++ u8 txRate;
++ u8 rateCode;
++ s8 rssiAck;
++ u8 Xretries;
++ u8 retries;
++ s8 rcRssiLast;
++ s8 rcRssiLastLkup;
++ s8 rcRssiLastPrev;
++ s8 rcRssiLastPrev2;
++ u8 rcProbeRate;
++ u8 rcRateMax;
++ s8 useTurboPrime;
++ s8 currentBoostState;
++ u8 rcHwMaxRetryRate;
++ u8 ac;
++ u8 resvd[2];
++ s8 rcRssiThres[RATE_TABLE_SIZE];
++ u8 rcPer[RATE_TABLE_SIZE];
++ u8 resv2[RATE_TABLE_SIZE + 5];
++ int32_t misc[0]; /* Can be used for HT specific or other misc info */
++};
++
++#define TXCTL_OFFSET(ah) (AR_SREV_9300_20_OR_LATER(ah) ? 11 : 2)
++#define TXCTL_NUMWORDS(ah) (AR_SREV_5416_20_OR_LATER(ah) ? 12 : 8)
++#define TXSTATUS_OFFSET(ah) (AR_SREV_9300_20_OR_LATER(ah) ? 2 : 14)
++#define TXSTATUS_NUMWORDS(ah) (AR_SREV_9300_20_OR_LATER(ah) ? 7 : 10)
++
++#define RXCTL_OFFSET(ah) (AR_SREV_9300_20_OR_LATER(ah) ? 0 : 3)
++#define RXCTL_NUMWORDS(ah) (AR_SREV_9300_20_OR_LATER(ah) ? 0 : 1)
++#define RXSTATUS_OFFSET(ah) (AR_SREV_9300_20_OR_LATER(ah) ? 1 : 4)
++#define RXSTATUS_NUMWORDS(ah) (AR_SREV_9300_20_OR_LATER(ah) ? 11 : 9)
++
++struct ath_desc_info {
++ u8 txctl_offset;
++ u8 txctl_numwords;
++ u8 txstatus_offset;
++ u8 txstatus_numwords;
++ u8 rxctl_offset;
++ u8 rxctl_numwords;
++ u8 rxstatus_offset;
++ u8 rxstatus_numwords;
++};
++
++#define PKTLOG_MOV_RD_IDX(_rd_offset, _log_buf, _log_size) \
++ do { \
++ if ((_rd_offset + sizeof(struct ath_pktlog_hdr) + \
++ ((struct ath_pktlog_hdr *)((_log_buf)->log_data + \
++ (_rd_offset)))->size) <= _log_size) { \
++ _rd_offset = ((_rd_offset) + \
++ sizeof(struct ath_pktlog_hdr) + \
++ ((struct ath_pktlog_hdr *) \
++ ((_log_buf)->log_data + \
++ (_rd_offset)))->size); \
++ } else { \
++ _rd_offset = ((struct ath_pktlog_hdr *) \
++ ((_log_buf)->log_data + \
++ (_rd_offset)))->size; \
++ } \
++ (_rd_offset) = (((_log_size) - (_rd_offset)) >= \
++ sizeof(struct ath_pktlog_hdr)) ? \
++ _rd_offset : 0; \
++ } while (0);
++
++struct ath_pktlog_bufhdr {
++ u32 magic_num; /* Used by post processing scripts */
++ u32 version; /* Set to CUR_PKTLOG_VER */
++};
++
++struct ath_pktlog_buf {
++ struct ath_pktlog_bufhdr bufhdr;
++ int32_t rd_offset;
++ int32_t wr_offset;
++ char log_data[0];
++};
++
++struct ath_pktlog {
++ struct ath_pktlog_buf *pktlog_buf;
++ u32 pktlog_filter;
++ u32 pktlog_buf_size; /* Size of buffer in bytes */
++ spinlock_t pktlog_lock;
++};
++
++struct ath_pktlog_debugfs {
++ struct dentry *debugfs_pktlog;
++ struct dentry *pktlog_enable;
++ struct dentry *pktlog_start;
++ struct dentry *pktlog_filter;
++ struct dentry *pktlog_size;
++ struct dentry *pktlog_dump;
++ struct ath_pktlog pktlog;
++};
++
++void ath_pktlog_txctl(struct ath_softc *sc, struct ath_buf *bf);
++void ath_pktlog_txstatus(struct ath_softc *sc, void *ds);
++void ath_pktlog_rx(struct ath_softc *sc, void *ds, struct sk_buff *skb);
++void ath9k_pktlog_rc(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv,
++ int8_t ratecode, u8 rate, int8_t is_probing, u16 ac);
++void ath9k_pktlog_rcupdate(struct ath_softc *sc,
++ struct ath_rate_priv *ath_rc_priv, u8 tx_rate,
++ u8 rate_code, u8 xretries, u8 retries, int8_t rssi,
++ u16 ac);
++void ath9k_pktlog_txcomplete(struct ath_softc *sc ,struct list_head *bf_head,
++ struct ath_buf *bf, struct ath_buf *bf_last);
++void ath9k_pktlog_txctrl(struct ath_softc *sc, struct list_head *bf_head,
++ struct ath_buf *lastbf);
++int ath9k_init_pktlog(struct ath_softc *sc);
++void ath9k_deinit_pktlog(struct ath_softc *sc);
++#else /* CONFIG_ATH9K_PKTLOG */
++static inline void ath_pktlog_txstatus(struct ath_softc *sc, void *ds)
++{
++}
++
++static inline void ath_pktlog_rx(struct ath_softc *sc, void *ds,
++ struct sk_buff *skb)
++{
++}
++
++static inline void ath9k_pktlog_rc(struct ath_softc *sc,
++ struct ath_rate_priv *ath_rc_priv,
++ int8_t ratecode, u8 rate,
++ int8_t is_probing, u16 ac)
++{
++}
++
++static inline void ath9k_pktlog_rcupdate(struct ath_softc *sc,
++ struct ath_rate_priv *ath_rc_priv,
++ u8 tx_rate, u8 rate_code,
++ u8 xretries, u8 retries,
++ int8_t rssi, u16 ac)
++{
++}
++
++static inline void ath9k_pktlog_txcomplete(struct ath_softc *sc,
++ struct list_head *bf_head,
++ struct ath_buf *bf,
++ struct ath_buf *bf_last)
++{
++}
++
++static inline void ath9k_pktlog_txctrl(struct ath_softc *sc,
++ struct list_head *bf_head,
++ struct ath_buf *lastbf)
++{
++}
++static inline int ath9k_init_pktlog(struct ath_softc *sc)
++{
++ return 0;
++}
++static inline void ath9k_deinit_pktlog(struct ath_softc *sc)
++{
++}
++#endif /* CONFIG_ATH9K_PKTLOG */
++
++#endif
+--- a/drivers/net/wireless/ath/ath9k/rc.c
++++ b/drivers/net/wireless/ath/ath9k/rc.c
+@@ -580,7 +580,7 @@ static u8 ath_rc_setvalid_htrates(struct
+ static u8 ath_rc_get_highest_rix(struct ath_softc *sc,
+ struct ath_rate_priv *ath_rc_priv,
+ const struct ath_rate_table *rate_table,
+- int *is_probing)
++ int *is_probing, u16 ac)
+ {
+ u32 best_thruput, this_thruput, now_msec;
+ u8 rate, next_rate, best_rate, maxindex, minindex;
+@@ -671,6 +671,8 @@ static u8 ath_rc_get_highest_rix(struct
+
+ rate = ath_rc_priv->valid_rate_index[0];
+
++ ath9k_pktlog_rc(sc, ath_rc_priv, rate_table->info[rate].ratecode,
++ rate, *is_probing, ac);
+ return rate;
+ }
+
+@@ -762,7 +764,7 @@ static void ath_get_rate(void *priv, str
+ try_per_rate = 4;
+
+ rate_table = ath_rc_priv->rate_table;
+- rix = ath_rc_get_highest_rix(sc, ath_rc_priv, rate_table, &is_probe);
++ rix = ath_rc_get_highest_rix(sc, ath_rc_priv, rate_table, &is_probe, skb_get_queue_mapping(skb));
+
+ /*
+ * If we're in HT mode and both us and our peer supports LDPC.
+@@ -1012,7 +1014,8 @@ static void ath_debug_stat_retries(struc
+ static void ath_rc_update_ht(struct ath_softc *sc,
+ struct ath_rate_priv *ath_rc_priv,
+ struct ieee80211_tx_info *tx_info,
+- int tx_rate, int xretries, int retries)
++ int tx_rate, int xretries, int retries,
++ u16 ac)
+ {
+ u32 now_msec = jiffies_to_msecs(jiffies);
+ int rate;
+@@ -1081,6 +1084,9 @@ static void ath_rc_update_ht(struct ath_
+ ath_debug_stat_retries(ath_rc_priv, tx_rate, xretries, retries,
+ ath_rc_priv->per[tx_rate]);
+
++ ath9k_pktlog_rcupdate(sc, ath_rc_priv, tx_rate,
++ rate_table->info[tx_rate].ratecode,
++ xretries, retries, tx_info->status.ack_signal, ac);
+ }
+
+ static int ath_rc_get_rateindex(const struct ath_rate_table *rate_table,
+@@ -1113,7 +1119,8 @@ static int ath_rc_get_rateindex(const st
+ static void ath_rc_tx_status(struct ath_softc *sc,
+ struct ath_rate_priv *ath_rc_priv,
+ struct ieee80211_tx_info *tx_info,
+- int final_ts_idx, int xretries, int long_retry)
++ int final_ts_idx, int xretries, int long_retry,
++ u16 ac)
+ {
+ const struct ath_rate_table *rate_table;
+ struct ieee80211_tx_rate *rates = tx_info->status.rates;
+@@ -1142,7 +1149,7 @@ static void ath_rc_tx_status(struct ath_
+ rix = ath_rc_get_rateindex(rate_table, &rates[i]);
+ ath_rc_update_ht(sc, ath_rc_priv, tx_info,
+ rix, xretries ? 1 : 2,
+- rates[i].count);
++ rates[i].count, ac);
+ }
+ }
+ } else {
+@@ -1164,7 +1171,7 @@ static void ath_rc_tx_status(struct ath_
+ return;
+
+ rix = ath_rc_get_rateindex(rate_table, &rates[i]);
+- ath_rc_update_ht(sc, ath_rc_priv, tx_info, rix, xretries, long_retry);
++ ath_rc_update_ht(sc, ath_rc_priv, tx_info, rix, xretries, long_retry, ac);
+ }
+
+ static const
+@@ -1358,7 +1365,7 @@ static void ath_tx_status(void *priv, st
+ tx_status = 1;
+
+ ath_rc_tx_status(sc, ath_rc_priv, tx_info, final_ts_idx, tx_status,
+- long_retry);
++ long_retry, skb_get_queue_mapping(skb));
+
+ /* Check if aggregation has to be enabled for this tid */
+ if (conf_is_ht(&sc->hw->conf) &&
+--- a/drivers/net/wireless/ath/ath9k/recv.c
++++ b/drivers/net/wireless/ath/ath9k/recv.c
+@@ -1577,6 +1577,7 @@ int ath_rx_tasklet(struct ath_softc *sc,
+ struct ieee80211_rx_status *rxs;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
++ u32 *rx_desc = NULL;
+ /*
+ * The hw can technically differ from common->hw when using ath9k
+ * virtual wiphy so to account for that we iterate over the active
+@@ -1676,13 +1677,24 @@ int ath_rx_tasklet(struct ath_softc *sc,
+ dma_type);
+
+ skb_put(skb, rs.rs_datalen + ah->caps.rx_status_len);
+- if (ah->caps.rx_status_len)
++ if (ah->caps.rx_status_len) {
++ rx_desc = kzalloc(ah->caps.rx_status_len, GFP_ATOMIC);
++ if (rx_desc == NULL)
++ BUG_ON(1);
++ memcpy(rx_desc, skb->data, ah->caps.rx_status_len);
+ skb_pull(skb, ah->caps.rx_status_len);
++}
+
+ if (!rs.rs_more)
+ ath9k_rx_skb_postprocess(common, hdr_skb, &rs,
+ rxs, decrypt_error);
+
++ if (rx_desc) {
++ ath_pktlog_rx(sc, (void *) rx_desc, skb);
++ kfree(rx_desc);
++ } else
++ ath_pktlog_rx(sc, bf->bf_desc, skb);
++
+ /* We will now give hardware our shiny new allocated skb */
+ bf->bf_mpdu = requeue_skb;
+ bf->bf_buf_addr = dma_map_single(sc->dev, requeue_skb->data,
+--- a/drivers/net/wireless/ath/ath9k/xmit.c
++++ b/drivers/net/wireless/ath/ath9k/xmit.c
+@@ -475,6 +475,8 @@ static void ath_tx_complete_aggr(struct
+ list_move_tail(&bf->list, &bf_head);
+ }
+
++ ath9k_pktlog_txcomplete(sc, &bf_head, bf, bf_last);
++
+ if (!txpending || (tid->state & AGGR_CLEANUP)) {
+ /*
+ * complete the acked-ones/xretried ones; update
+@@ -2043,7 +2045,7 @@ static void ath_tx_processq(struct ath_s
+ ds = lastbf->bf_desc;
+
+ memset(&ts, 0, sizeof(ts));
+- status = ath9k_hw_txprocdesc(ah, ds, &ts);
++ status = ath9k_hw_txprocdesc(ah, ds, &ts, NULL);
+ if (status == -EINPROGRESS) {
+ spin_unlock_bh(&txq->axq_lock);
+ break;
+@@ -2085,11 +2087,15 @@ static void ath_tx_processq(struct ath_s
+ ath_tx_rc_status(sc, bf, &ts, 1, txok ? 0 : 1, txok, true);
+ }
+
+- if (bf_isampdu(bf))
++ if (bf_isampdu(bf)) {
+ ath_tx_complete_aggr(sc, txq, bf, &bf_head, &ts, txok,
+ true);
+- else
++ } else {
++ ath9k_pktlog_txctrl(sc, &bf_head, lastbf);
+ ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, txok, 0);
++ }
++
++ ath_pktlog_txstatus(sc, lastbf->bf_desc);
+
+ spin_lock_bh(&txq->axq_lock);
+
+@@ -2210,9 +2216,11 @@ void ath_tx_edma_tasklet(struct ath_soft
+ struct list_head bf_head;
+ int status;
+ int txok;
++ u32 txs_desc[9];
+
+ for (;;) {
+- status = ath9k_hw_txprocdesc(ah, NULL, (void *)&txs);
++ status = ath9k_hw_txprocdesc(ah, NULL, (void *)&txs,
++ (void *) txs_desc);
+ if (status == -EINPROGRESS)
+ break;
+ if (status == -EIO) {
+@@ -2258,9 +2266,13 @@ void ath_tx_edma_tasklet(struct ath_soft
+ if (bf_isampdu(bf))
+ ath_tx_complete_aggr(sc, txq, bf, &bf_head, &txs,
+ txok, true);
+- else
++ else {
++ ath9k_pktlog_txctrl(sc, &bf_head, lastbf);
+ ath_tx_complete_buf(sc, bf, txq, &bf_head,
+ &txs, txok, 0);
++ }
++
++ ath_pktlog_txstatus(sc, txs_desc);
+
+ spin_lock_bh(&txq->axq_lock);
+
diff --git a/crap/README b/crap/README
new file mode 100644
index 0000000..c1b512e
--- /dev/null
+++ b/crap/README
@@ -0,0 +1,17 @@
+
+compat-wireless crap patches
+============================
+
+If you are including patches into this directory you
+must be fixing some critical bug for a customer which needs
+immediate release or immediate testing.
+
+Alternatively you would use this to apply some sort of
+crap code you are maintaining.
+
+You must have a really good reason to be adding files
+in this directory. If possible you should explain your
+reasoning of why the patch is getting included here and
+not upstream and why it hasn't even yet been posted.
+
+You should avoid these patches at all costs.
diff --git a/defconfigs/README b/defconfigs/README
new file mode 100644
index 0000000..5dcd84a
--- /dev/null
+++ b/defconfigs/README
@@ -0,0 +1,2 @@
+You can stuff into this directory default config.mk files which are suited
+for debugging or other testing purposes.
diff --git a/defconfigs/atheros-debug.mk b/defconfigs/atheros-debug.mk
new file mode 100644
index 0000000..285b231
--- /dev/null
+++ b/defconfigs/atheros-debug.mk
@@ -0,0 +1,649 @@
+export
+
+## NOTE
+## Make sure to have each variable declaration start
+## in the first column, no whitespace allowed.
+
+ifeq ($(wildcard $(KLIB_BUILD)/.config),)
+# These will be ignored by compat autoconf
+ CONFIG_PCI=y
+ CONFIG_USB=y
+ CONFIG_PCMCIA=y
+ CONFIG_SSB=m
+else
+include $(KLIB_BUILD)/.config
+endif
+
+ifneq ($(wildcard $(KLIB_BUILD)/Makefile),)
+
+COMPAT_LATEST_VERSION = 1
+
+KERNEL_VERSION := $(shell $(MAKE) -C $(KLIB_BUILD) kernelversion | sed -n 's/^\([0-9]\)\..*/\1/p')
+
+ifneq ($(KERNEL_VERSION),2)
+KERNEL_SUBLEVEL := $(shell $(MAKE) -C $(KLIB_BUILD) kernelversion | sed -n 's/^3\.\([0-9]\+\).*/\1/p')
+else
+COMPAT_26LATEST_VERSION = 39
+KERNEL_26SUBLEVEL := $(shell $(MAKE) -C $(KLIB_BUILD) kernelversion | sed -n 's/^2\.6\.\([0-9]\+\).*/\1/p')
+COMPAT_26VERSIONS := $(shell I=$(COMPAT_26LATEST_VERSION); while [ "$$I" -gt $(KERNEL_26SUBLEVEL) ]; do echo $$I; I=$$(($$I - 1)); done)
+$(foreach ver,$(COMPAT_26VERSIONS),$(eval CONFIG_COMPAT_KERNEL_2_6_$(ver)=y))
+KERNEL_SUBLEVEL := -1
+endif
+
+COMPAT_VERSIONS := $(shell I=$(COMPAT_LATEST_VERSION); while [ "$$I" -gt $(KERNEL_SUBLEVEL) ]; do echo $$I; I=$$(($$I - 1)); done)
+$(foreach ver,$(COMPAT_VERSIONS),$(eval CONFIG_COMPAT_KERNEL_3_$(ver)=y))
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_24
+$(error "ERROR: compat-wireless by default supports kernels >= 2.6.24, try enabling only one driver though")
+endif #CONFIG_COMPAT_KERNEL_2_6_24
+
+ifeq ($(CONFIG_CFG80211),y)
+$(error "ERROR: your kernel has CONFIG_CFG80211=y, you should have it CONFIG_CFG80211=m if you want to use this thing.")
+endif
+
+
+# 2.6.27 has FTRACE_DYNAMIC borked, so we will complain if
+# you have it enabled, otherwise you will very likely run into
+# a kernel panic.
+ifeq ($(shell test $(KERNEL_VERSION) -eq 2 -a $(KERNEL_SUBLEVEL) -eq 27 && echo yes),yes)
+ifeq ($(CONFIG_DYNAMIC_FTRACE),y)
+$(error "ERROR: Your 2.6.27 kernel has CONFIG_DYNAMIC_FTRACE, please upgrade your distribution kernel as newer ones should not have this enabled (and if so report a bug) or remove this warning if you know what you are doing")
+endif
+endif
+
+# This is because with CONFIG_MAC80211 include/linux/skbuff.h will
+# enable on 2.6.27 a new attribute:
+#
+# skb->do_not_encrypt
+#
+# and on 2.6.28 another new attribute:
+#
+# skb->requeue
+#
+# In kernel 2.6.32 both attributes were removed.
+#
+ifeq ($(shell test $(KERNEL_VERSION) -eq 2 -a $(KERNEL_SUBLEVEL) -ge 27 -a $(KERNEL_SUBLEVEL) -le 31 && echo yes),yes)
+ifeq ($(CONFIG_MAC80211),)
+$(error "ERROR: Your >=2.6.27 and <= 2.6.31 kernel has CONFIG_MAC80211 disabled, you should have it CONFIG_MAC80211=m if you want to use this thing.")
+endif
+endif
+
+ifneq ($(KERNELRELEASE),) # This prevents a warning
+
+# We will warn when you don't have MQ support or NET_SCHED enabled.
+#
+# We could consider just quiting if MQ and NET_SCHED is disabled
+# as I suspect all users of this package want 802.11e (WME) and
+# 802.11n (HT) support.
+ifeq ($(CONFIG_NET_SCHED),)
+ QOS_REQS_MISSING+=CONFIG_NET_SCHED
+endif
+
+ifneq ($(QOS_REQS_MISSING),) # Complain about our missing dependencies
+$(warning "WARNING: You are running a kernel >= 2.6.23, you should enable in it $(QOS_REQS_MISSING) for 802.11[ne] support")
+endif
+
+endif # build check
+endif # kernel Makefile check
+
+# These both are needed by compat-wireless || compat-bluetooth so enable them
+ CONFIG_COMPAT_RFKILL=y
+
+ifeq ($(CONFIG_MAC80211),y)
+$(error "ERROR: you have MAC80211 compiled into the kernel, CONFIG_MAC80211=y, as such you cannot replace its mac80211 driver. You need this set to CONFIG_MAC80211=m. If you are using Fedora upgrade your kernel as later version should this set as modular. For further information on Fedora see https://bugzilla.redhat.com/show_bug.cgi?id=470143. If you are using your own kernel recompile it and make mac80211 modular")
+else
+ CONFIG_COMPAT_WIRELESS=y
+ CONFIG_COMPAT_WIRELESS_MODULES=m
+ CONFIG_COMPAT_VAR_MODULES=m
+# We could technically separate these but not yet, we only have b44
+# Note that we don't intend on backporting network drivers that
+# use Multiqueue as that was a pain to backport to kernels older than
+# 2.6.27. But -- we could just disable those drivers from kernels
+# older than 2.6.27
+ CONFIG_COMPAT_NETWORK_MODULES=m
+ CONFIG_COMPAT_NET_USB_MODULES=m
+endif
+
+# The Bluetooth compatibility only builds on kernels >= 2.6.27 for now
+ifndef CONFIG_COMPAT_KERNEL_2_6_27
+ifeq ($(CONFIG_BT),y)
+# we'll ignore compiling bluetooth
+else
+ CONFIG_COMPAT_BLUETOOTH=y
+ CONFIG_COMPAT_BLUETOOTH_MODULES=m
+endif
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_33
+ifdef CONFIG_FW_LOADER
+ CONFIG_COMPAT_FIRMWARE_CLASS=m
+endif #CONFIG_FW_LOADER
+endif #CONFIG_COMPAT_KERNEL_2_6_33
+
+# Wireless subsystem stuff
+CONFIG_MAC80211=m
+
+CONFIG_MAC80211_DEBUGFS=y
+CONFIG_MAC80211_NOINLINE=y
+CONFIG_MAC80211_VERBOSE_DEBUG=y
+CONFIG_MAC80211_HT_DEBUG=y
+# CONFIG_MAC80211_TKIP_DEBUG=y
+# CONFIG_MAC80211_IBSS_DEBUG=y
+CONFIG_MAC80211_VERBOSE_PS_DEBUG=y
+# CONFIG_MAC80211_VERBOSE_MPL_DEBUG=y
+# CONFIG_MAC80211_VERBOSE_MHWMP_DEBUG=y
+CONFIG_MAC80211_DEBUG_COUNTERS=y
+# CONFIG_MAC80211_DRIVER_API_TRACER=y
+
+# choose between pid and minstrel as default rate control algorithm
+CONFIG_MAC80211_RC_DEFAULT=minstrel_ht
+CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y
+# CONFIG_MAC80211_RC_DEFAULT_PID=y
+# This is the one used by our compat-wireless net/mac80211/rate.c
+# in case you have and old kernel which is overriding this to pid.
+CONFIG_COMPAT_MAC80211_RC_DEFAULT=minstrel_ht
+CONFIG_MAC80211_RC_PID=y
+CONFIG_MAC80211_RC_MINSTREL=y
+CONFIG_MAC80211_RC_MINSTREL_HT=y
+ifdef CONFIG_LEDS_TRIGGERS
+CONFIG_MAC80211_LEDS=y
+endif #CONFIG_LEDS_TRIGGERS
+
+# enable mesh networking too
+CONFIG_MAC80211_MESH=y
+
+CONFIG_CFG80211=m
+CONFIG_CFG80211_DEFAULT_PS=y
+CONFIG_CFG80211_DEBUGFS=y
+# CONFIG_NL80211_TESTMODE=y
+CONFIG_CFG80211_DEVELOPER_WARNINGS=y
+CONFIG_CFG80211_REG_DEBUG=y
+# See below for wext stuff
+
+CONFIG_LIB80211=m
+CONFIG_LIB80211_CRYPT_WEP=m
+CONFIG_LIB80211_CRYPT_CCMP=m
+CONFIG_LIB80211_CRYPT_TKIP=m
+# CONFIG_LIB80211_DEBUG=y
+
+CONFIG_BT=m
+CONFIG_COMPAT_BT_L2CAP=y
+CONFIG_COMPAT_BT_SCO=y
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+# CONFIG_BT_CMTP depends on ISDN_CAPI
+ifdef CONFIG_ISDN_CAPI
+CONFIG_BT_CMTP=m
+endif #CONFIG_ISDN_CAPI
+ifndef CONFIG_COMPAT_KERNEL_2_6_28
+CONFIG_COMPAT_BT_HIDP=m
+endif #CONFIG_COMPAT_KERNEL_2_6_28
+
+CONFIG_BT_HCIUART=M
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+CONFIG_BT_HCIUART_ATH3K=y
+CONFIG_BT_HCIUART_LL=y
+
+CONFIG_BT_HCIVHCI=m
+CONFIG_BT_MRVL=m
+
+ifdef CONFIG_PCMCIA
+CONFIG_BT_HCIDTL1=m
+CONFIG_BT_HCIBT3C=m
+CONFIG_BT_HCIBLUECARD=m
+CONFIG_BT_HCIBTUART=m
+endif #CONFIG_PCMCIA
+
+
+# We need CONFIG_WIRELESS_EXT for CONFIG_CFG80211_WEXT for every kernel
+# version. The new way CONFIG_CFG80211_WEXT is called from the kernel
+# does not work with compat-wireless because it calls some callback
+# function on struct wiphy. This struct is shipped with compat-wireless
+# and changes from kernel version to version. We are using the
+# wireless_handlers attribute which will be activated by
+# CONFIG_WIRELESS_EXT.
+ifdef CONFIG_WIRELESS_EXT
+CONFIG_CFG80211_WEXT=y
+else #CONFIG_CFG80211_WEXT
+$(warning "WARNING: CONFIG_CFG80211_WEXT will be deactivated or not working because kernel was compiled with CONFIG_WIRELESS_EXT=n. Tools using wext interface like iwconfig will not work. To activate it build your kernel e.g. with CONFIG_LIBIPW=m.")
+endif #CONFIG_WIRELESS_EXT
+
+ifdef CONFIG_STAGING
+CONFIG_COMPAT_STAGING=m
+endif #CONFIG_STAGING
+
+# mac80211 test driver
+CONFIG_MAC80211_HWSIM=m
+
+CONFIG_ATH5K=m
+CONFIG_ATH5K_DEBUG=y
+# CONFIG_ATH5K_AHB=y
+
+CONFIG_ATH9K=m
+CONFIG_ATH9K_HW=m
+CONFIG_ATH9K_COMMON=m
+CONFIG_ATH9K_DEBUGFS=y
+# CONFIG_ATH9K_AHB=y
+CONFIG_ATH9K_PKTLOG=y
+
+# Disable this to get minstrel as default, we leave the ath9k
+# rate control algorithm as the default for now as that is also
+# default upstream on the kernel. We will move this to minstrel
+# as default once we get minstrel properly tested and blessed by
+# our systems engineering team. CCK rates also need to be used
+# for long range considerations.
+CONFIG_ATH9K_RATE_CONTROL=y
+
+# PCI Drivers
+ifdef CONFIG_PCI
+
+CONFIG_ATH5K_PCI=y
+CONFIG_ATH9K_PCI=y
+
+CONFIG_IWLAGN=m
+# CONFIG_IWLWIFI_DEBUG=y
+# CONFIG_IWLWIFI_DEBUGFS=y
+# CONFIG_IWLWIFI_DEVICE_TRACING=y
+# CONFIG_IWLWIFI_DEBUG_EXPERIMENTAL_UCODE=y
+CONFIG_IWL_P2P=y
+
+CONFIG_IWLWIFI_LEGACY=m
+CONFIG_COMPAT_IWL4965=m
+CONFIG_IWL3945=m
+# CONFIG_IWLWIFI_LEGACY_DEBUG=y
+# CONFIG_IWLWIFI_LEGACY_DEBUGFS=y
+# CONFIG_IWLWIFI_LEGACY_DEVICE_TRACING=y
+
+
+CONFIG_B43=m
+CONFIG_B43_HWRNG=y
+CONFIG_B43_PCI_AUTOSELECT=y
+ifdef CONFIG_PCMCIA
+CONFIG_B43_PCMCIA=y
+endif #CONFIG_PCMCIA
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_B43_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+CONFIG_B43_PHY_LP=y
+CONFIG_B43_PHY_N=y
+# CONFIG_B43_FORCE_PIO=y
+# CONFIG_B43_DEBUG=y
+
+CONFIG_B43LEGACY=m
+CONFIG_B43LEGACY_HWRNG=y
+CONFIG_B43LEGACY_PCI_AUTOSELECT=y
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_B43LEGACY_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+# CONFIG_B43LEGACY_DEBUG=y
+CONFIG_B43LEGACY_DMA=y
+CONFIG_B43LEGACY_PIO=y
+
+ifdef CONFIG_WIRELESS_EXT
+# The Intel ipws
+CONFIG_LIBIPW=m
+# CONFIG_LIBIPW_DEBUG=y
+
+CONFIG_IPW2100=m
+CONFIG_IPW2100_MONITOR=y
+# CONFIG_IPW2100_DEBUG=y
+CONFIG_IPW2200=m
+CONFIG_IPW2200_MONITOR=y
+CONFIG_IPW2200_RADIOTAP=y
+CONFIG_IPW2200_PROMISCUOUS=y
+CONFIG_IPW2200_QOS=y
+# CONFIG_IPW2200_DEBUG=y
+# The above enables use a second interface prefixed 'rtap'.
+# Example usage:
+#
+# % modprobe ipw2200 rtap_iface=1
+# % ifconfig rtap0 up
+# % tethereal -i rtap0
+#
+# If you do not specify 'rtap_iface=1' as a module parameter then
+# the rtap interface will not be created and you will need to turn
+# it on via sysfs:
+#
+# % echo 1 > /sys/bus/pci/drivers/ipw2200/*/rtap_iface
+endif #CONFIG_WIRELESS_EXT
+
+ifdef CONFIG_SSB
+# Sonics Silicon Backplane
+CONFIG_SSB_SPROM=y
+
+CONFIG_SSB_BLOCKIO=y
+CONFIG_SSB_PCIHOST=y
+CONFIG_SSB_B43_PCI_BRIDGE=y
+ifdef CONFIG_PCMCIA
+CONFIG_SSB_PCMCIAHOST=y
+endif #CONFIG_PCMCIA
+# CONFIG_SSB_DEBUG=y
+CONFIG_SSB_DRIVER_PCICORE=y
+endif #CONFIG_SSB
+
+CONFIG_BCMA=m
+CONFIG_BCMA_BLOCKIO=y
+CONFIG_BCMA_HOST_PCI=y
+# CONFIG_BCMA_DEBUG=y
+
+CONFIG_P54_PCI=m
+
+CONFIG_B44=m
+CONFIG_B44_PCI=y
+
+CONFIG_RTL8180=m
+
+CONFIG_ADM8211=m
+
+CONFIG_RT2X00_LIB_PCI=m
+CONFIG_RT2400PCI=m
+CONFIG_RT2500PCI=m
+ifdef CONFIG_CRC_CCITT
+CONFIG_RT2800PCI=m
+CONFIG_RT2800PCI_RT33XX=y
+CONFIG_RT2800PCI_RT35XX=y
+# CONFIG_RT2800PCI_RT53XX=y
+endif #CONFIG_CRC_CCITT
+NEED_RT2X00=y
+
+# Two rt2x00 drivers require firmware: rt61pci and rt73usb. They depend on
+# CRC to check the firmware. We check here first for the PCI
+# driver as we're in the PCI section.
+ifdef CONFIG_CRC_ITU_T
+CONFIG_RT61PCI=m
+endif #CONFIG_CRC_ITU_T
+
+CONFIG_MWL8K=m
+
+# Ethernet drivers go here
+CONFIG_ATL1=m
+CONFIG_ATL2=m
+CONFIG_ATL1E=m
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_ATL1C=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_ATL1C=m
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+ifdef CONFIG_WIRELESS_EXT
+CONFIG_HERMES=m
+CONFIG_HERMES_CACHE_FW_ON_INIT=y
+ifdef CONFIG_PPC_PMAC
+CONFIG_APPLE_AIRPORT=m
+endif #CONFIG_PPC_PMAC
+CONFIG_PLX_HERMES=m
+CONFIG_TMD_HERMES=m
+CONFIG_NORTEL_HERMES=m
+CONFIG_PCI_HERMES=m
+ifdef CONFIG_PCMCIA
+CONFIG_PCMCIA_HERMES=m
+CONFIG_PCMCIA_SPECTRUM=m
+endif #CONFIG_PCMCIA
+endif #CONFIG_WIRELESS_EXT
+
+CONFIG_RTL8192CE=m
+CONFIG_RTL8192SE=m
+
+ifdef CONFIG_COMPAT_STAGING
+CONFIG_BRCMSMAC=m
+endif #CONFIG_COMPAT_STAGING
+
+endif #CONFIG_PCI
+## end of PCI
+
+ifdef CONFIG_PCMCIA
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS=n
+CONFIG_LIBERTAS_CS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_CS=m
+NEED_LIBERTAS=y
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+endif #CONFIG_PCMCIA
+## end of PCMCIA
+
+# This is required for some cards
+CONFIG_EEPROM_93CX6=m
+
+# USB Drivers
+ifdef CONFIG_USB
+ifndef CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_COMPAT_ZD1211RW=m
+# CONFIG_ZD1211RW_DEBUG=y
+endif #CONFIG_COMPAT_KERNEL_2_6_29
+
+# Sorry, rndis_wlan uses cancel_work_sync which is new and can't be done in compat...
+
+# Wireless RNDIS USB support (RTL8185 802.11g) A-Link WL54PC
+# All of these devices are based on Broadcom 4320 chip which
+# is only wireless RNDIS chip known to date.
+# Note: this depends on CONFIG_USB_NET_RNDIS_HOST and CONFIG_USB_NET_CDCETHER
+# it also requires new RNDIS_HOST and CDC_ETHER modules which we add
+ifdef CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_USB_COMPAT_USBNET=n
+CONFIG_USB_NET_COMPAT_RNDIS_HOST=n
+CONFIG_USB_NET_COMPAT_RNDIS_WLAN=n
+CONFIG_USB_NET_COMPAT_CDCETHER=n
+else #CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_USB_COMPAT_USBNET=m
+ifdef CONFIG_USB_NET_CDCETHER
+CONFIG_USB_NET_COMPAT_RNDIS_HOST=m
+CONFIG_USB_NET_COMPAT_RNDIS_WLAN=m
+endif #CONFIG_USB_NET_CDCETHER
+ifdef CONFIG_USB_NET_CDCETHER_MODULE
+CONFIG_USB_NET_COMPAT_RNDIS_HOST=m
+CONFIG_USB_NET_COMPAT_RNDIS_WLAN=m
+endif #CONFIG_USB_NET_CDCETHER
+CONFIG_USB_NET_COMPAT_CDCETHER=m
+endif #CONFIG_COMPAT_KERNEL_2_6_29
+
+
+CONFIG_P54_USB=m
+CONFIG_RTL8187=m
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_RTL8187_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+
+CONFIG_AT76C50X_USB=m
+
+ifndef CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_CARL9170=m
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_CARL9170_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+CONFIG_CARL9170_DEBUGFS=y
+CONFIG_CARL9170_WPC=y
+endif #CONFIG_COMPAT_KERNEL_2_6_29
+
+# This activates a threading fix for usb urb.
+# this is mainline commit: b3e670443b7fb8a2d29831b62b44a039c283e351
+# This fix will be included in some stable releases.
+CONFIG_COMPAT_USB_URB_THREAD_FIX=y
+
+CONFIG_ATH9K_HTC=m
+CONFIG_ATH9K_HTC_DEBUGFS=y
+
+# RT2500USB does not require firmware
+CONFIG_RT2500USB=m
+ifdef CONFIG_CRC_CCITT
+CONFIG_RT2800USB=m
+CONFIG_RT2800USB_RT33XX=y
+CONFIG_RT2800USB_RT35XX=y
+# CONFIG_RT2800USB_RT53XX=y
+CONFIG_RT2800USB_UNKNOWN=y
+endif #CONFIG_CRC_CCITT
+CONFIG_RT2X00_LIB_USB=m
+NEED_RT2X00=y
+# RT73USB requires firmware
+ifdef CONFIG_CRC_ITU_T
+CONFIG_RT73USB=m
+endif #CONFIG_CRC_ITU_T
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_THINFIRM_USB=n
+CONFIG_LIBERTAS_USB=n
+NEED_LIBERTAS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_THINFIRM_USB=m
+CONFIG_LIBERTAS_USB=m
+NEED_LIBERTAS=y
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+CONFIG_ORINOCO_USB=m
+
+CONFIG_BT_HCIBTUSB=m
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_ATH3K=m
+
+CONFIG_RTL8192CU=m
+
+endif #CONFIG_USB end of USB driver list
+
+ifdef CONFIG_SPI_MASTER
+ifndef CONFIG_COMPAT_KERNEL_2_6_25
+
+ifdef CONFIG_CRC7
+CONFIG_WL1251_SPI=m
+CONFIG_WL12XX_SPI=m
+endif #CONFIG_CRC7
+CONFIG_P54_SPI=m
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_SPI=n
+NEED_LIBERTAS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_SPI=m
+NEED_LIBERTAS=y
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+endif #CONFIG_COMPAT_KERNEL_2_6_25
+endif #CONFIG_SPI_MASTER end of SPI driver list
+
+ifdef CONFIG_MMC
+
+CONFIG_SSB_SDIOHOST=y
+CONFIG_B43_SDIO=y
+
+ifdef CONFIG_CRC7
+ifdef CONFIG_WL12XX_PLATFORM_DATA
+CONFIG_COMPAT_WL1251_SDIO=m
+endif #CONFIG_WL12XX_PLATFORM_DATA
+
+ifndef CONFIG_COMPAT_KERNEL_2_6_32
+ifdef CONFIG_WL12XX_PLATFORM_DATA
+CONFIG_COMPAT_WL12XX_SDIO=m
+endif #CONFIG_WL12XX_PLATFORM_DATA
+endif #CONFIG_COMPAT_KERNEL_2_6_32
+
+endif #CONFIG_CRC7
+
+CONFIG_MWIFIEX_SDIO=m
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_SDIO=n
+NEED_LIBERTAS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_SDIO=m
+NEED_LIBERTAS=y
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+CONFIG_IWM=m
+# CONFIG_IWM_DEBUG=y
+
+CONFIG_BT_HCIBTSDIO=m
+CONFIG_BT_MRVL_SDIO=m
+
+ifdef CONFIG_COMPAT_STAGING
+ifdef CONFIG_WIRELESS_EXT
+# CONFIG_ATH6KL_DISABLE_TARGET_DBGLOGS is not set
+# CONFIG_ATH6KL_ENABLE_COEXISTENCE is not set
+# CONFIG_ATH6KL_ENABLE_HOST_DEBUG is not set
+CONFIG_ATH6KL_ENABLE_TARGET_DEBUG_PRINTS=y
+# CONFIG_ATH6KL_HCI_BRIDGE is not set
+# CONFIG_ATH6KL_HTC_RAW_INTERFACE is not set
+# CONFIG_ATH6KL_SKIP_ABI_VERSION_CHECK is not set
+CONFIG_ATH6KL_VIRTUAL_SCATTER_GATHER=y
+CONFIG_ATH6K_LEGACY=m
+
+CONFIG_BRCMFMAC=m
+
+endif #CONFIG_WIRELESS_EXT
+endif #CONFIG_COMPAT_STAGING
+
+
+endif #CONFIG_MMC
+
+CONFIG_RTLWIFI=m
+CONFIG_RTL8192C_COMMON=m
+
+# Common rt2x00 requirements
+ifeq ($(NEED_RT2X00),y)
+CONFIG_RT2X00=y
+CONFIG_RT2X00_LIB=m
+CONFIG_RT2800_LIB=m
+CONFIG_RT2X00_LIB_FIRMWARE=y
+CONFIG_RT2X00_LIB_CRYPTO=y
+# CONFIG_RT2X00_LIB_SOC=y
+ifdef CONFIG_COMPAT_KERNEL_2_6_25
+CONFIG_RT2X00_LIB_LEDS=n
+else #CONFIG_COMPAT_KERNEL_2_6_25
+ifdef CONFIG_LEDS_CLASS
+CONFIG_RT2X00_LIB_LEDS=y
+endif #CONFIG_LEDS_CLASS
+endif #CONFIG_COMPAT_KERNEL_2_6_25
+# CONFIG_RT2X00_DEBUG=y
+# CONFIG_RT2X00_LIB_DEBUGFS
+endif
+
+# p54
+CONFIG_P54_COMMON=m
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_P54_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+
+# Atheros
+CONFIG_ATH_COMMON=m
+CONFIG_ATH_DEBUG=y
+
+ifdef CONFIG_COMPAT_STAGING
+CONFIG_BRCMUTIL=m
+# CONFIG_BRCMDBG=y
+endif #CONFIG_COMPAT_STAGING
+
+ifdef CONFIG_CRC7
+CONFIG_WL1251=m
+CONFIG_WL12XX=m
+CONFIG_WL12XX_HT=y
+endif #CONFIG_CRC7
+
+CONFIG_MWIFIEX=m
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+ifeq ($(NEED_LIBERTAS),y)
+CONFIG_LIBERTAS_THINFIRM=m
+CONFIG_LIBERTAS=m
+CONFIG_LIBERTAS_MESH=y
+# CONFIG_LIBERTAS_DEBUG=y
+endif
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+# We need the backported rfkill module on kernel < 2.6.31.
+# In more recent kernel versions use the in kernel rfkill module.
+ifdef CONFIG_COMPAT_KERNEL_2_6_31
+CONFIG_RFKILL_BACKPORT=m
+ifdef CONFIG_LEDS_TRIGGERS
+CONFIG_RFKILL_BACKPORT_LEDS=y
+endif #CONFIG_LEDS_TRIGGERS
+CONFIG_RFKILL_BACKPORT_INPUT=y
+endif #CONFIG_COMPAT_KERNEL_2_6_31
+
diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig
new file mode 100644
index 0000000..c1172da
--- /dev/null
+++ b/drivers/bcma/Kconfig
@@ -0,0 +1,57 @@
+config BCMA_POSSIBLE
+ bool
+ depends on HAS_IOMEM && HAS_DMA
+ default y
+
+menu "Broadcom specific AMBA"
+ depends on BCMA_POSSIBLE
+
+config BCMA
+ tristate "BCMA support"
+ depends on BCMA_POSSIBLE
+ help
+ Bus driver for Broadcom specific Advanced Microcontroller Bus
+ Architecture.
+
+# Support for Block-I/O. SELECT this from the driver that needs it.
+config BCMA_BLOCKIO
+ bool
+ depends on BCMA
+
+config BCMA_HOST_PCI_POSSIBLE
+ bool
+ depends on BCMA && PCI = y
+ default y
+
+config BCMA_HOST_PCI
+ bool "Support for BCMA on PCI-host bus"
+ depends on BCMA_HOST_PCI_POSSIBLE
+
+config BCMA_DRIVER_PCI_HOSTMODE
+ bool "Driver for PCI core working in hostmode"
+ depends on BCMA && MIPS
+ help
+ PCI core hostmode operation (external PCI bus).
+
+config BCMA_HOST_SOC
+ bool
+ depends on BCMA_DRIVER_MIPS
+
+config BCMA_DRIVER_MIPS
+ bool "BCMA Broadcom MIPS core driver"
+ depends on BCMA && MIPS
+ help
+ Driver for the Broadcom MIPS core attached to Broadcom specific
+ Advanced Microcontroller Bus.
+
+ If unsure, say N
+
+config BCMA_DEBUG
+ bool "BCMA debugging"
+ depends on BCMA
+ help
+ This turns on additional debugging messages.
+
+ If unsure, say N
+
+endmenu
diff --git a/drivers/bcma/Makefile b/drivers/bcma/Makefile
new file mode 100644
index 0000000..82de24e
--- /dev/null
+++ b/drivers/bcma/Makefile
@@ -0,0 +1,10 @@
+bcma-y += main.o scan.o core.o sprom.o
+bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o
+bcma-y += driver_pci.o
+bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o
+bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o
+bcma-$(CONFIG_BCMA_HOST_PCI) += host_pci.o
+bcma-$(CONFIG_BCMA_HOST_SOC) += host_soc.o
+obj-$(CONFIG_BCMA) += bcma.o
+
+ccflags-$(CONFIG_BCMA_DEBUG) := -DDEBUG
diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
new file mode 100644
index 0000000..30a3085
--- /dev/null
+++ b/drivers/bcma/bcma_private.h
@@ -0,0 +1,51 @@
+#ifndef LINUX_BCMA_PRIVATE_H_
+#define LINUX_BCMA_PRIVATE_H_
+
+#ifndef pr_fmt
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#endif
+
+#include <linux/bcma/bcma.h>
+#include <linux/delay.h>
+
+#define BCMA_CORE_SIZE 0x1000
+
+struct bcma_bus;
+
+/* main.c */
+int bcma_bus_register(struct bcma_bus *bus);
+void bcma_bus_unregister(struct bcma_bus *bus);
+int __init bcma_bus_early_register(struct bcma_bus *bus,
+ struct bcma_device *core_cc,
+ struct bcma_device *core_mips);
+
+/* scan.c */
+int bcma_bus_scan(struct bcma_bus *bus);
+int __init bcma_bus_scan_early(struct bcma_bus *bus,
+ struct bcma_device_id *match,
+ struct bcma_device *core);
+void bcma_init_bus(struct bcma_bus *bus);
+
+/* sprom.c */
+int bcma_sprom_get(struct bcma_bus *bus);
+
+/* driver_chipcommon.c */
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+void bcma_chipco_serial_init(struct bcma_drv_cc *cc);
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
+
+/* driver_chipcommon_pmu.c */
+u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc);
+u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc);
+
+#ifdef CONFIG_BCMA_HOST_PCI
+/* host_pci.c */
+extern int __init bcma_host_pci_init(void);
+extern void __exit bcma_host_pci_exit(void);
+#endif /* CONFIG_BCMA_HOST_PCI */
+
+#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
+void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc);
+#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
+
+#endif
diff --git a/drivers/bcma/core.c b/drivers/bcma/core.c
new file mode 100644
index 0000000..893f6e0
--- /dev/null
+++ b/drivers/bcma/core.c
@@ -0,0 +1,127 @@
+/*
+ * Broadcom specific AMBA
+ * Core ops
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/export.h>
+#include <linux/bcma/bcma.h>
+
+bool bcma_core_is_enabled(struct bcma_device *core)
+{
+ if ((bcma_aread32(core, BCMA_IOCTL) & (BCMA_IOCTL_CLK | BCMA_IOCTL_FGC))
+ != BCMA_IOCTL_CLK)
+ return false;
+ if (bcma_aread32(core, BCMA_RESET_CTL) & BCMA_RESET_CTL_RESET)
+ return false;
+ return true;
+}
+EXPORT_SYMBOL_GPL(bcma_core_is_enabled);
+
+void bcma_core_disable(struct bcma_device *core, u32 flags)
+{
+ if (bcma_aread32(core, BCMA_RESET_CTL) & BCMA_RESET_CTL_RESET)
+ return;
+
+ bcma_awrite32(core, BCMA_IOCTL, flags);
+ bcma_aread32(core, BCMA_IOCTL);
+ udelay(10);
+
+ bcma_awrite32(core, BCMA_RESET_CTL, BCMA_RESET_CTL_RESET);
+ udelay(1);
+}
+EXPORT_SYMBOL_GPL(bcma_core_disable);
+
+int bcma_core_enable(struct bcma_device *core, u32 flags)
+{
+ bcma_core_disable(core, flags);
+
+ bcma_awrite32(core, BCMA_IOCTL, (BCMA_IOCTL_CLK | BCMA_IOCTL_FGC | flags));
+ bcma_aread32(core, BCMA_IOCTL);
+
+ bcma_awrite32(core, BCMA_RESET_CTL, 0);
+ udelay(1);
+
+ bcma_awrite32(core, BCMA_IOCTL, (BCMA_IOCTL_CLK | flags));
+ bcma_aread32(core, BCMA_IOCTL);
+ udelay(1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(bcma_core_enable);
+
+void bcma_core_set_clockmode(struct bcma_device *core,
+ enum bcma_clkmode clkmode)
+{
+ u16 i;
+
+ WARN_ON(core->id.id != BCMA_CORE_CHIPCOMMON &&
+ core->id.id != BCMA_CORE_PCIE &&
+ core->id.id != BCMA_CORE_80211);
+
+ switch (clkmode) {
+ case BCMA_CLKMODE_FAST:
+ bcma_set32(core, BCMA_CLKCTLST, BCMA_CLKCTLST_FORCEHT);
+ udelay(64);
+ for (i = 0; i < 1500; i++) {
+ if (bcma_read32(core, BCMA_CLKCTLST) &
+ BCMA_CLKCTLST_HAVEHT) {
+ i = 0;
+ break;
+ }
+ udelay(10);
+ }
+ if (i)
+ pr_err("HT force timeout\n");
+ break;
+ case BCMA_CLKMODE_DYNAMIC:
+ pr_warn("Dynamic clockmode not supported yet!\n");
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(bcma_core_set_clockmode);
+
+void bcma_core_pll_ctl(struct bcma_device *core, u32 req, u32 status, bool on)
+{
+ u16 i;
+
+ WARN_ON(req & ~BCMA_CLKCTLST_EXTRESREQ);
+ WARN_ON(status & ~BCMA_CLKCTLST_EXTRESST);
+
+ if (on) {
+ bcma_set32(core, BCMA_CLKCTLST, req);
+ for (i = 0; i < 10000; i++) {
+ if ((bcma_read32(core, BCMA_CLKCTLST) & status) ==
+ status) {
+ i = 0;
+ break;
+ }
+ udelay(10);
+ }
+ if (i)
+ pr_err("PLL enable timeout\n");
+ } else {
+ pr_warn("Disabling PLL not supported yet!\n");
+ }
+}
+EXPORT_SYMBOL_GPL(bcma_core_pll_ctl);
+
+u32 bcma_core_dma_translation(struct bcma_device *core)
+{
+ switch (core->bus->hosttype) {
+ case BCMA_HOSTTYPE_SOC:
+ return 0;
+ case BCMA_HOSTTYPE_PCI:
+ if (bcma_aread32(core, BCMA_IOST) & BCMA_IOST_DMA64)
+ return BCMA_DMA_TRANSLATION_DMA64_CMT;
+ else
+ return BCMA_DMA_TRANSLATION_DMA32_CMT;
+ default:
+ pr_err("DMA translation unknown for host %d\n",
+ core->bus->hosttype);
+ }
+ return BCMA_DMA_TRANSLATION_NONE;
+}
+EXPORT_SYMBOL(bcma_core_dma_translation);
diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
new file mode 100644
index 0000000..e9f1b3f
--- /dev/null
+++ b/drivers/bcma/driver_chipcommon.c
@@ -0,0 +1,157 @@
+/*
+ * Broadcom specific AMBA
+ * ChipCommon core driver
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/export.h>
+#include <linux/bcma/bcma.h>
+
+static inline u32 bcma_cc_write32_masked(struct bcma_drv_cc *cc, u16 offset,
+ u32 mask, u32 value)
+{
+ value &= mask;
+ value |= bcma_cc_read32(cc, offset) & ~mask;
+ bcma_cc_write32(cc, offset, value);
+
+ return value;
+}
+
+void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
+{
+ u32 leddc_on = 10;
+ u32 leddc_off = 90;
+
+ if (cc->setup_done)
+ return;
+
+ if (cc->core->id.rev >= 11)
+ cc->status = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT);
+ cc->capabilities = bcma_cc_read32(cc, BCMA_CC_CAP);
+ if (cc->core->id.rev >= 35)
+ cc->capabilities_ext = bcma_cc_read32(cc, BCMA_CC_CAP_EXT);
+
+ if (cc->core->id.rev >= 20) {
+ bcma_cc_write32(cc, BCMA_CC_GPIOPULLUP, 0);
+ bcma_cc_write32(cc, BCMA_CC_GPIOPULLDOWN, 0);
+ }
+
+ if (cc->capabilities & BCMA_CC_CAP_PMU)
+ bcma_pmu_init(cc);
+ if (cc->capabilities & BCMA_CC_CAP_PCTL)
+ pr_err("Power control not implemented!\n");
+
+ if (cc->core->id.rev >= 16) {
+ if (cc->core->bus->sprom.leddc_on_time &&
+ cc->core->bus->sprom.leddc_off_time) {
+ leddc_on = cc->core->bus->sprom.leddc_on_time;
+ leddc_off = cc->core->bus->sprom.leddc_off_time;
+ }
+ bcma_cc_write32(cc, BCMA_CC_GPIOTIMER,
+ ((leddc_on << BCMA_CC_GPIOTIMER_ONTIME_SHIFT) |
+ (leddc_off << BCMA_CC_GPIOTIMER_OFFTIME_SHIFT)));
+ }
+
+ cc->setup_done = true;
+}
+
+/* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */
+void bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks)
+{
+ /* instant NMI */
+ bcma_cc_write32(cc, BCMA_CC_WATCHDOG, ticks);
+}
+
+void bcma_chipco_irq_mask(struct bcma_drv_cc *cc, u32 mask, u32 value)
+{
+ bcma_cc_write32_masked(cc, BCMA_CC_IRQMASK, mask, value);
+}
+
+u32 bcma_chipco_irq_status(struct bcma_drv_cc *cc, u32 mask)
+{
+ return bcma_cc_read32(cc, BCMA_CC_IRQSTAT) & mask;
+}
+
+u32 bcma_chipco_gpio_in(struct bcma_drv_cc *cc, u32 mask)
+{
+ return bcma_cc_read32(cc, BCMA_CC_GPIOIN) & mask;
+}
+
+u32 bcma_chipco_gpio_out(struct bcma_drv_cc *cc, u32 mask, u32 value)
+{
+ return bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUT, mask, value);
+}
+
+u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value)
+{
+ return bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUTEN, mask, value);
+}
+
+u32 bcma_chipco_gpio_control(struct bcma_drv_cc *cc, u32 mask, u32 value)
+{
+ return bcma_cc_write32_masked(cc, BCMA_CC_GPIOCTL, mask, value);
+}
+EXPORT_SYMBOL_GPL(bcma_chipco_gpio_control);
+
+u32 bcma_chipco_gpio_intmask(struct bcma_drv_cc *cc, u32 mask, u32 value)
+{
+ return bcma_cc_write32_masked(cc, BCMA_CC_GPIOIRQ, mask, value);
+}
+
+u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value)
+{
+ return bcma_cc_write32_masked(cc, BCMA_CC_GPIOPOL, mask, value);
+}
+
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+void bcma_chipco_serial_init(struct bcma_drv_cc *cc)
+{
+ unsigned int irq;
+ u32 baud_base;
+ u32 i;
+ unsigned int ccrev = cc->core->id.rev;
+ struct bcma_serial_port *ports = cc->serial_ports;
+
+ if (ccrev >= 11 && ccrev != 15) {
+ /* Fixed ALP clock */
+ baud_base = bcma_pmu_alp_clock(cc);
+ if (ccrev >= 21) {
+ /* Turn off UART clock before switching clocksource. */
+ bcma_cc_write32(cc, BCMA_CC_CORECTL,
+ bcma_cc_read32(cc, BCMA_CC_CORECTL)
+ & ~BCMA_CC_CORECTL_UARTCLKEN);
+ }
+ /* Set the override bit so we don't divide it */
+ bcma_cc_write32(cc, BCMA_CC_CORECTL,
+ bcma_cc_read32(cc, BCMA_CC_CORECTL)
+ | BCMA_CC_CORECTL_UARTCLK0);
+ if (ccrev >= 21) {
+ /* Re-enable the UART clock. */
+ bcma_cc_write32(cc, BCMA_CC_CORECTL,
+ bcma_cc_read32(cc, BCMA_CC_CORECTL)
+ | BCMA_CC_CORECTL_UARTCLKEN);
+ }
+ } else {
+ pr_err("serial not supported on this device ccrev: 0x%x\n",
+ ccrev);
+ return;
+ }
+
+ irq = bcma_core_mips_irq(cc->core);
+
+ /* Determine the registers of the UARTs */
+ cc->nr_serial_ports = (cc->capabilities & BCMA_CC_CAP_NRUART);
+ for (i = 0; i < cc->nr_serial_ports; i++) {
+ ports[i].regs = cc->core->io_addr + BCMA_CC_UART0_DATA +
+ (i * 256);
+ ports[i].irq = irq;
+ ports[i].baud_base = baud_base;
+ ports[i].reg_shift = 0;
+ }
+}
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c
new file mode 100644
index 0000000..800163c
--- /dev/null
+++ b/drivers/bcma/driver_chipcommon_pmu.c
@@ -0,0 +1,310 @@
+/*
+ * Broadcom specific AMBA
+ * ChipCommon Power Management Unit driver
+ *
+ * Copyright 2009, Michael Buesch <m@bues.ch>
+ * Copyright 2007, Broadcom Corporation
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/export.h>
+#include <linux/bcma/bcma.h>
+
+static u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset)
+{
+ bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
+ bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
+ return bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA);
+}
+
+void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, u32 value)
+{
+ bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
+ bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
+ bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, value);
+}
+EXPORT_SYMBOL_GPL(bcma_chipco_pll_write);
+
+void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask,
+ u32 set)
+{
+ bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
+ bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
+ bcma_cc_maskset32(cc, BCMA_CC_PLLCTL_DATA, mask, set);
+}
+EXPORT_SYMBOL_GPL(bcma_chipco_pll_maskset);
+
+void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc,
+ u32 offset, u32 mask, u32 set)
+{
+ bcma_cc_write32(cc, BCMA_CC_CHIPCTL_ADDR, offset);
+ bcma_cc_read32(cc, BCMA_CC_CHIPCTL_ADDR);
+ bcma_cc_maskset32(cc, BCMA_CC_CHIPCTL_DATA, mask, set);
+}
+EXPORT_SYMBOL_GPL(bcma_chipco_chipctl_maskset);
+
+void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask,
+ u32 set)
+{
+ bcma_cc_write32(cc, BCMA_CC_REGCTL_ADDR, offset);
+ bcma_cc_read32(cc, BCMA_CC_REGCTL_ADDR);
+ bcma_cc_maskset32(cc, BCMA_CC_REGCTL_DATA, mask, set);
+}
+EXPORT_SYMBOL_GPL(bcma_chipco_regctl_maskset);
+
+static void bcma_pmu_pll_init(struct bcma_drv_cc *cc)
+{
+ struct bcma_bus *bus = cc->core->bus;
+
+ switch (bus->chipinfo.id) {
+ case 0x4313:
+ case 0x4331:
+ case 43224:
+ case 43225:
+ break;
+ default:
+ pr_err("PLL init unknown for device 0x%04X\n",
+ bus->chipinfo.id);
+ }
+}
+
+static void bcma_pmu_resources_init(struct bcma_drv_cc *cc)
+{
+ struct bcma_bus *bus = cc->core->bus;
+ u32 min_msk = 0, max_msk = 0;
+
+ switch (bus->chipinfo.id) {
+ case 0x4313:
+ min_msk = 0x200D;
+ max_msk = 0xFFFF;
+ break;
+ case 43224:
+ case 43225:
+ break;
+ default:
+ pr_err("PMU resource config unknown for device 0x%04X\n",
+ bus->chipinfo.id);
+ }
+
+ /* Set the resource masks. */
+ if (min_msk)
+ bcma_cc_write32(cc, BCMA_CC_PMU_MINRES_MSK, min_msk);
+ if (max_msk)
+ bcma_cc_write32(cc, BCMA_CC_PMU_MAXRES_MSK, max_msk);
+}
+
+void bcma_pmu_swreg_init(struct bcma_drv_cc *cc)
+{
+ struct bcma_bus *bus = cc->core->bus;
+
+ switch (bus->chipinfo.id) {
+ case 0x4313:
+ case 0x4331:
+ case 43224:
+ case 43225:
+ break;
+ default:
+ pr_err("PMU switch/regulators init unknown for device "
+ "0x%04X\n", bus->chipinfo.id);
+ }
+}
+
+/* Disable to allow reading SPROM. Don't know the adventages of enabling it. */
+void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable)
+{
+ struct bcma_bus *bus = cc->core->bus;
+ u32 val;
+
+ val = bcma_cc_read32(cc, BCMA_CC_CHIPCTL);
+ if (enable) {
+ val |= BCMA_CHIPCTL_4331_EXTPA_EN;
+ if (bus->chipinfo.pkg == 9 || bus->chipinfo.pkg == 11)
+ val |= BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5;
+ } else {
+ val &= ~BCMA_CHIPCTL_4331_EXTPA_EN;
+ val &= ~BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5;
+ }
+ bcma_cc_write32(cc, BCMA_CC_CHIPCTL, val);
+}
+
+void bcma_pmu_workarounds(struct bcma_drv_cc *cc)
+{
+ struct bcma_bus *bus = cc->core->bus;
+
+ switch (bus->chipinfo.id) {
+ case 0x4313:
+ bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x7);
+ break;
+ case 0x4331:
+ /* BCM4331 workaround is SPROM-related, we put it in sprom.c */
+ break;
+ case 43224:
+ if (bus->chipinfo.rev == 0) {
+ pr_err("Workarounds for 43224 rev 0 not fully "
+ "implemented\n");
+ bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x00F000F0);
+ } else {
+ bcma_chipco_chipctl_maskset(cc, 0, ~0, 0xF0);
+ }
+ break;
+ case 43225:
+ break;
+ default:
+ pr_err("Workarounds unknown for device 0x%04X\n",
+ bus->chipinfo.id);
+ }
+}
+
+void bcma_pmu_init(struct bcma_drv_cc *cc)
+{
+ u32 pmucap;
+
+ pmucap = bcma_cc_read32(cc, BCMA_CC_PMU_CAP);
+ cc->pmu.rev = (pmucap & BCMA_CC_PMU_CAP_REVISION);
+
+ pr_debug("Found rev %u PMU (capabilities 0x%08X)\n", cc->pmu.rev,
+ pmucap);
+
+ if (cc->pmu.rev == 1)
+ bcma_cc_mask32(cc, BCMA_CC_PMU_CTL,
+ ~BCMA_CC_PMU_CTL_NOILPONW);
+ else
+ bcma_cc_set32(cc, BCMA_CC_PMU_CTL,
+ BCMA_CC_PMU_CTL_NOILPONW);
+
+ if (cc->core->id.id == 0x4329 && cc->core->id.rev == 2)
+ pr_err("Fix for 4329b0 bad LPOM state not implemented!\n");
+
+ bcma_pmu_pll_init(cc);
+ bcma_pmu_resources_init(cc);
+ bcma_pmu_swreg_init(cc);
+ bcma_pmu_workarounds(cc);
+}
+
+u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc)
+{
+ struct bcma_bus *bus = cc->core->bus;
+
+ switch (bus->chipinfo.id) {
+ case 0x4716:
+ case 0x4748:
+ case 47162:
+ case 0x4313:
+ case 0x5357:
+ case 0x4749:
+ case 53572:
+ /* always 20Mhz */
+ return 20000 * 1000;
+ case 0x5356:
+ case 0x5300:
+ /* always 25Mhz */
+ return 25000 * 1000;
+ default:
+ pr_warn("No ALP clock specified for %04X device, "
+ "pmu rev. %d, using default %d Hz\n",
+ bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_ALP_CLOCK);
+ }
+ return BCMA_CC_PMU_ALP_CLOCK;
+}
+
+/* Find the output of the "m" pll divider given pll controls that start with
+ * pllreg "pll0" i.e. 12 for main 6 for phy, 0 for misc.
+ */
+static u32 bcma_pmu_clock(struct bcma_drv_cc *cc, u32 pll0, u32 m)
+{
+ u32 tmp, div, ndiv, p1, p2, fc;
+ struct bcma_bus *bus = cc->core->bus;
+
+ BUG_ON((pll0 & 3) || (pll0 > BCMA_CC_PMU4716_MAINPLL_PLL0));
+
+ BUG_ON(!m || m > 4);
+
+ if (bus->chipinfo.id == 0x5357 || bus->chipinfo.id == 0x4749) {
+ /* Detect failure in clock setting */
+ tmp = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT);
+ if (tmp & 0x40000)
+ return 133 * 1000000;
+ }
+
+ tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_P1P2_OFF);
+ p1 = (tmp & BCMA_CC_PPL_P1_MASK) >> BCMA_CC_PPL_P1_SHIFT;
+ p2 = (tmp & BCMA_CC_PPL_P2_MASK) >> BCMA_CC_PPL_P2_SHIFT;
+
+ tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_M14_OFF);
+ div = (tmp >> ((m - 1) * BCMA_CC_PPL_MDIV_WIDTH)) &
+ BCMA_CC_PPL_MDIV_MASK;
+
+ tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_NM5_OFF);
+ ndiv = (tmp & BCMA_CC_PPL_NDIV_MASK) >> BCMA_CC_PPL_NDIV_SHIFT;
+
+ /* Do calculation in Mhz */
+ fc = bcma_pmu_alp_clock(cc) / 1000000;
+ fc = (p1 * ndiv * fc) / p2;
+
+ /* Return clock in Hertz */
+ return (fc / div) * 1000000;
+}
+
+/* query bus clock frequency for PMU-enabled chipcommon */
+u32 bcma_pmu_get_clockcontrol(struct bcma_drv_cc *cc)
+{
+ struct bcma_bus *bus = cc->core->bus;
+
+ switch (bus->chipinfo.id) {
+ case 0x4716:
+ case 0x4748:
+ case 47162:
+ return bcma_pmu_clock(cc, BCMA_CC_PMU4716_MAINPLL_PLL0,
+ BCMA_CC_PMU5_MAINPLL_SSB);
+ case 0x5356:
+ return bcma_pmu_clock(cc, BCMA_CC_PMU5356_MAINPLL_PLL0,
+ BCMA_CC_PMU5_MAINPLL_SSB);
+ case 0x5357:
+ case 0x4749:
+ return bcma_pmu_clock(cc, BCMA_CC_PMU5357_MAINPLL_PLL0,
+ BCMA_CC_PMU5_MAINPLL_SSB);
+ case 0x5300:
+ return bcma_pmu_clock(cc, BCMA_CC_PMU4706_MAINPLL_PLL0,
+ BCMA_CC_PMU5_MAINPLL_SSB);
+ case 53572:
+ return 75000000;
+ default:
+ pr_warn("No backplane clock specified for %04X device, "
+ "pmu rev. %d, using default %d Hz\n",
+ bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_HT_CLOCK);
+ }
+ return BCMA_CC_PMU_HT_CLOCK;
+}
+
+/* query cpu clock frequency for PMU-enabled chipcommon */
+u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc)
+{
+ struct bcma_bus *bus = cc->core->bus;
+
+ if (bus->chipinfo.id == 53572)
+ return 300000000;
+
+ if (cc->pmu.rev >= 5) {
+ u32 pll;
+ switch (bus->chipinfo.id) {
+ case 0x5356:
+ pll = BCMA_CC_PMU5356_MAINPLL_PLL0;
+ break;
+ case 0x5357:
+ case 0x4749:
+ pll = BCMA_CC_PMU5357_MAINPLL_PLL0;
+ break;
+ default:
+ pll = BCMA_CC_PMU4716_MAINPLL_PLL0;
+ break;
+ }
+
+ /* TODO: if (bus->chipinfo.id == 0x5300)
+ return si_4706_pmu_clock(sih, osh, cc, PMU4706_MAINPLL_PLL0, PMU5_MAINPLL_CPU); */
+ return bcma_pmu_clock(cc, pll, BCMA_CC_PMU5_MAINPLL_CPU);
+ }
+
+ return bcma_pmu_get_clockcontrol(cc);
+}
diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c
new file mode 100644
index 0000000..c3e9dff
--- /dev/null
+++ b/drivers/bcma/driver_mips.c
@@ -0,0 +1,256 @@
+/*
+ * Broadcom specific AMBA
+ * Broadcom MIPS32 74K core driver
+ *
+ * Copyright 2009, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
+ * Copyright 2010, Bernhard Loos <bernhardloos@googlemail.com>
+ * Copyright 2011, Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+
+#include <linux/bcma/bcma.h>
+
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+
+/* The 47162a0 hangs when reading MIPS DMP registers registers */
+static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev)
+{
+ return dev->bus->chipinfo.id == 47162 && dev->bus->chipinfo.rev == 0 &&
+ dev->id.id == BCMA_CORE_MIPS_74K;
+}
+
+/* The 5357b0 hangs when reading USB20H DMP registers */
+static inline bool bcma_core_mips_bcm5357b0_quirk(struct bcma_device *dev)
+{
+ return (dev->bus->chipinfo.id == 0x5357 ||
+ dev->bus->chipinfo.id == 0x4749) &&
+ dev->bus->chipinfo.pkg == 11 &&
+ dev->id.id == BCMA_CORE_USB20_HOST;
+}
+
+static inline u32 mips_read32(struct bcma_drv_mips *mcore,
+ u16 offset)
+{
+ return bcma_read32(mcore->core, offset);
+}
+
+static inline void mips_write32(struct bcma_drv_mips *mcore,
+ u16 offset,
+ u32 value)
+{
+ bcma_write32(mcore->core, offset, value);
+}
+
+static const u32 ipsflag_irq_mask[] = {
+ 0,
+ BCMA_MIPS_IPSFLAG_IRQ1,
+ BCMA_MIPS_IPSFLAG_IRQ2,
+ BCMA_MIPS_IPSFLAG_IRQ3,
+ BCMA_MIPS_IPSFLAG_IRQ4,
+};
+
+static const u32 ipsflag_irq_shift[] = {
+ 0,
+ BCMA_MIPS_IPSFLAG_IRQ1_SHIFT,
+ BCMA_MIPS_IPSFLAG_IRQ2_SHIFT,
+ BCMA_MIPS_IPSFLAG_IRQ3_SHIFT,
+ BCMA_MIPS_IPSFLAG_IRQ4_SHIFT,
+};
+
+static u32 bcma_core_mips_irqflag(struct bcma_device *dev)
+{
+ u32 flag;
+
+ if (bcma_core_mips_bcm47162a0_quirk(dev))
+ return dev->core_index;
+ if (bcma_core_mips_bcm5357b0_quirk(dev))
+ return dev->core_index;
+ flag = bcma_aread32(dev, BCMA_MIPS_OOBSELOUTA30);
+
+ return flag & 0x1F;
+}
+
+/* Get the MIPS IRQ assignment for a specified device.
+ * If unassigned, 0 is returned.
+ */
+unsigned int bcma_core_mips_irq(struct bcma_device *dev)
+{
+ struct bcma_device *mdev = dev->bus->drv_mips.core;
+ u32 irqflag;
+ unsigned int irq;
+
+ irqflag = bcma_core_mips_irqflag(dev);
+
+ for (irq = 1; irq <= 4; irq++)
+ if (bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq)) &
+ (1 << irqflag))
+ return irq;
+
+ return 0;
+}
+EXPORT_SYMBOL(bcma_core_mips_irq);
+
+static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq)
+{
+ unsigned int oldirq = bcma_core_mips_irq(dev);
+ struct bcma_bus *bus = dev->bus;
+ struct bcma_device *mdev = bus->drv_mips.core;
+ u32 irqflag;
+
+ irqflag = bcma_core_mips_irqflag(dev);
+ BUG_ON(oldirq == 6);
+
+ dev->irq = irq + 2;
+
+ /* clear the old irq */
+ if (oldirq == 0)
+ bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0),
+ bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) &
+ ~(1 << irqflag));
+ else
+ bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq), 0);
+
+ /* assign the new one */
+ if (irq == 0) {
+ bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0),
+ bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) |
+ (1 << irqflag));
+ } else {
+ u32 oldirqflag = bcma_read32(mdev,
+ BCMA_MIPS_MIPS74K_INTMASK(irq));
+ if (oldirqflag) {
+ struct bcma_device *core;
+
+ /* backplane irq line is in use, find out who uses
+ * it and set user to irq 0
+ */
+ list_for_each_entry_reverse(core, &bus->cores, list) {
+ if ((1 << bcma_core_mips_irqflag(core)) ==
+ oldirqflag) {
+ bcma_core_mips_set_irq(core, 0);
+ break;
+ }
+ }
+ }
+ bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq),
+ 1 << irqflag);
+ }
+
+ pr_info("set_irq: core 0x%04x, irq %d => %d\n",
+ dev->id.id, oldirq + 2, irq + 2);
+}
+
+static void bcma_core_mips_print_irq(struct bcma_device *dev, unsigned int irq)
+{
+ int i;
+ static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"};
+ printk(KERN_INFO KBUILD_MODNAME ": core 0x%04x, irq :", dev->id.id);
+ for (i = 0; i <= 6; i++)
+ printk(" %s%s", irq_name[i], i == irq ? "*" : " ");
+ printk("\n");
+}
+
+static void bcma_core_mips_dump_irq(struct bcma_bus *bus)
+{
+ struct bcma_device *core;
+
+ list_for_each_entry_reverse(core, &bus->cores, list) {
+ bcma_core_mips_print_irq(core, bcma_core_mips_irq(core));
+ }
+}
+
+u32 bcma_cpu_clock(struct bcma_drv_mips *mcore)
+{
+ struct bcma_bus *bus = mcore->core->bus;
+
+ if (bus->drv_cc.capabilities & BCMA_CC_CAP_PMU)
+ return bcma_pmu_get_clockcpu(&bus->drv_cc);
+
+ pr_err("No PMU available, need this to get the cpu clock\n");
+ return 0;
+}
+EXPORT_SYMBOL(bcma_cpu_clock);
+
+static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore)
+{
+ struct bcma_bus *bus = mcore->core->bus;
+
+ switch (bus->drv_cc.capabilities & BCMA_CC_CAP_FLASHT) {
+ case BCMA_CC_FLASHT_STSER:
+ case BCMA_CC_FLASHT_ATSER:
+ pr_err("Serial flash not supported.\n");
+ break;
+ case BCMA_CC_FLASHT_PARA:
+ pr_info("found parallel flash.\n");
+ bus->drv_cc.pflash.window = 0x1c000000;
+ bus->drv_cc.pflash.window_size = 0x02000000;
+
+ if ((bcma_read32(bus->drv_cc.core, BCMA_CC_FLASH_CFG) &
+ BCMA_CC_FLASH_CFG_DS) == 0)
+ bus->drv_cc.pflash.buswidth = 1;
+ else
+ bus->drv_cc.pflash.buswidth = 2;
+ break;
+ default:
+ pr_err("flash not supported.\n");
+ }
+}
+
+void bcma_core_mips_init(struct bcma_drv_mips *mcore)
+{
+ struct bcma_bus *bus;
+ struct bcma_device *core;
+ bus = mcore->core->bus;
+
+ pr_info("Initializing MIPS core...\n");
+
+ if (!mcore->setup_done)
+ mcore->assigned_irqs = 1;
+
+ /* Assign IRQs to all cores on the bus */
+ list_for_each_entry_reverse(core, &bus->cores, list) {
+ int mips_irq;
+ if (core->irq)
+ continue;
+
+ mips_irq = bcma_core_mips_irq(core);
+ if (mips_irq > 4)
+ core->irq = 0;
+ else
+ core->irq = mips_irq + 2;
+ if (core->irq > 5)
+ continue;
+ switch (core->id.id) {
+ case BCMA_CORE_PCI:
+ case BCMA_CORE_PCIE:
+ case BCMA_CORE_ETHERNET:
+ case BCMA_CORE_ETHERNET_GBIT:
+ case BCMA_CORE_MAC_GBIT:
+ case BCMA_CORE_80211:
+ case BCMA_CORE_USB20_HOST:
+ /* These devices get their own IRQ line if available,
+ * the rest goes on IRQ0
+ */
+ if (mcore->assigned_irqs <= 4)
+ bcma_core_mips_set_irq(core,
+ mcore->assigned_irqs++);
+ break;
+ }
+ }
+ pr_info("IRQ reconfiguration done\n");
+ bcma_core_mips_dump_irq(bus);
+
+ if (mcore->setup_done)
+ return;
+
+ bcma_chipco_serial_init(&bus->drv_cc);
+ bcma_core_mips_flash_detect(mcore);
+ mcore->setup_done = true;
+}
diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c
new file mode 100644
index 0000000..4fde625
--- /dev/null
+++ b/drivers/bcma/driver_pci.c
@@ -0,0 +1,238 @@
+/*
+ * Broadcom specific AMBA
+ * PCI Core
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/export.h>
+#include <linux/bcma/bcma.h>
+
+/**************************************************
+ * R/W ops.
+ **************************************************/
+
+static u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address)
+{
+ pcicore_write32(pc, 0x130, address);
+ pcicore_read32(pc, 0x130);
+ return pcicore_read32(pc, 0x134);
+}
+
+#if 0
+static void bcma_pcie_write(struct bcma_drv_pci *pc, u32 address, u32 data)
+{
+ pcicore_write32(pc, 0x130, address);
+ pcicore_read32(pc, 0x130);
+ pcicore_write32(pc, 0x134, data);
+}
+#endif
+
+static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy)
+{
+ const u16 mdio_control = 0x128;
+ const u16 mdio_data = 0x12C;
+ u32 v;
+ int i;
+
+ v = (1 << 30); /* Start of Transaction */
+ v |= (1 << 28); /* Write Transaction */
+ v |= (1 << 17); /* Turnaround */
+ v |= (0x1F << 18);
+ v |= (phy << 4);
+ pcicore_write32(pc, mdio_data, v);
+
+ udelay(10);
+ for (i = 0; i < 200; i++) {
+ v = pcicore_read32(pc, mdio_control);
+ if (v & 0x100 /* Trans complete */)
+ break;
+ msleep(1);
+ }
+}
+
+static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 device, u8 address)
+{
+ const u16 mdio_control = 0x128;
+ const u16 mdio_data = 0x12C;
+ int max_retries = 10;
+ u16 ret = 0;
+ u32 v;
+ int i;
+
+ v = 0x80; /* Enable Preamble Sequence */
+ v |= 0x2; /* MDIO Clock Divisor */
+ pcicore_write32(pc, mdio_control, v);
+
+ if (pc->core->id.rev >= 10) {
+ max_retries = 200;
+ bcma_pcie_mdio_set_phy(pc, device);
+ }
+
+ v = (1 << 30); /* Start of Transaction */
+ v |= (1 << 29); /* Read Transaction */
+ v |= (1 << 17); /* Turnaround */
+ if (pc->core->id.rev < 10)
+ v |= (u32)device << 22;
+ v |= (u32)address << 18;
+ pcicore_write32(pc, mdio_data, v);
+ /* Wait for the device to complete the transaction */
+ udelay(10);
+ for (i = 0; i < max_retries; i++) {
+ v = pcicore_read32(pc, mdio_control);
+ if (v & 0x100 /* Trans complete */) {
+ udelay(10);
+ ret = pcicore_read32(pc, mdio_data);
+ break;
+ }
+ msleep(1);
+ }
+ pcicore_write32(pc, mdio_control, 0);
+ return ret;
+}
+
+static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device,
+ u8 address, u16 data)
+{
+ const u16 mdio_control = 0x128;
+ const u16 mdio_data = 0x12C;
+ int max_retries = 10;
+ u32 v;
+ int i;
+
+ v = 0x80; /* Enable Preamble Sequence */
+ v |= 0x2; /* MDIO Clock Divisor */
+ pcicore_write32(pc, mdio_control, v);
+
+ if (pc->core->id.rev >= 10) {
+ max_retries = 200;
+ bcma_pcie_mdio_set_phy(pc, device);
+ }
+
+ v = (1 << 30); /* Start of Transaction */
+ v |= (1 << 28); /* Write Transaction */
+ v |= (1 << 17); /* Turnaround */
+ if (pc->core->id.rev < 10)
+ v |= (u32)device << 22;
+ v |= (u32)address << 18;
+ v |= data;
+ pcicore_write32(pc, mdio_data, v);
+ /* Wait for the device to complete the transaction */
+ udelay(10);
+ for (i = 0; i < max_retries; i++) {
+ v = pcicore_read32(pc, mdio_control);
+ if (v & 0x100 /* Trans complete */)
+ break;
+ msleep(1);
+ }
+ pcicore_write32(pc, mdio_control, 0);
+}
+
+/**************************************************
+ * Workarounds.
+ **************************************************/
+
+static u8 bcma_pcicore_polarity_workaround(struct bcma_drv_pci *pc)
+{
+ return (bcma_pcie_read(pc, 0x204) & 0x10) ? 0xC0 : 0x80;
+}
+
+static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc)
+{
+ const u8 serdes_pll_device = 0x1D;
+ const u8 serdes_rx_device = 0x1F;
+ u16 tmp;
+
+ bcma_pcie_mdio_write(pc, serdes_rx_device, 1 /* Control */,
+ bcma_pcicore_polarity_workaround(pc));
+ tmp = bcma_pcie_mdio_read(pc, serdes_pll_device, 1 /* Control */);
+ if (tmp & 0x4000)
+ bcma_pcie_mdio_write(pc, serdes_pll_device, 1, tmp & ~0x4000);
+}
+
+/**************************************************
+ * Init.
+ **************************************************/
+
+static void bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc)
+{
+ bcma_pcicore_serdes_workaround(pc);
+}
+
+static bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc)
+{
+ struct bcma_bus *bus = pc->core->bus;
+ u16 chipid_top;
+
+ chipid_top = (bus->chipinfo.id & 0xFF00);
+ if (chipid_top != 0x4700 &&
+ chipid_top != 0x5300)
+ return false;
+
+#ifdef CONFIG_SSB_DRIVER_PCICORE
+ if (bus->sprom.boardflags_lo & SSB_BFL_NOPCI)
+ return false;
+#endif /* CONFIG_SSB_DRIVER_PCICORE */
+
+#if 0
+ /* TODO: on BCMA we use address from EROM instead of magic formula */
+ u32 tmp;
+ return !mips_busprobe32(tmp, (bus->mmio +
+ (pc->core->core_index * BCMA_CORE_SIZE)));
+#endif
+
+ return true;
+}
+
+void bcma_core_pci_init(struct bcma_drv_pci *pc)
+{
+ if (pc->setup_done)
+ return;
+
+ if (bcma_core_pci_is_in_hostmode(pc)) {
+#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
+ bcma_core_pci_hostmode_init(pc);
+#else
+ pr_err("Driver compiled without support for hostmode PCI\n");
+#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
+ } else {
+ bcma_core_pci_clientmode_init(pc);
+ }
+
+ pc->setup_done = true;
+}
+
+int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
+ bool enable)
+{
+ struct pci_dev *pdev = pc->core->bus->host_pci;
+ u32 coremask, tmp;
+ int err = 0;
+
+ if (core->bus->hosttype != BCMA_HOSTTYPE_PCI) {
+ /* This bcma device is not on a PCI host-bus. So the IRQs are
+ * not routed through the PCI core.
+ * So we must not enable routing through the PCI core. */
+ goto out;
+ }
+
+ err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
+ if (err)
+ goto out;
+
+ coremask = BIT(core->core_index) << 8;
+ if (enable)
+ tmp |= coremask;
+ else
+ tmp &= ~coremask;
+
+ err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp);
+
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl);
diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c
new file mode 100644
index 0000000..eb332b7
--- /dev/null
+++ b/drivers/bcma/driver_pci_host.c
@@ -0,0 +1,14 @@
+/*
+ * Broadcom specific AMBA
+ * PCI Core in hostmode
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/bcma/bcma.h>
+
+void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc)
+{
+ pr_err("No support for PCI core in hostmode yet\n");
+}
diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
new file mode 100644
index 0000000..1b51d8b
--- /dev/null
+++ b/drivers/bcma/host_pci.c
@@ -0,0 +1,252 @@
+/*
+ * Broadcom specific AMBA
+ * PCI Host
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/slab.h>
+#include <linux/bcma/bcma.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+
+static void bcma_host_pci_switch_core(struct bcma_device *core)
+{
+ pci_write_config_dword(core->bus->host_pci, BCMA_PCI_BAR0_WIN,
+ core->addr);
+ pci_write_config_dword(core->bus->host_pci, BCMA_PCI_BAR0_WIN2,
+ core->wrap);
+ core->bus->mapped_core = core;
+ pr_debug("Switched to core: 0x%X\n", core->id.id);
+}
+
+static u8 bcma_host_pci_read8(struct bcma_device *core, u16 offset)
+{
+ if (core->bus->mapped_core != core)
+ bcma_host_pci_switch_core(core);
+ return ioread8(core->bus->mmio + offset);
+}
+
+static u16 bcma_host_pci_read16(struct bcma_device *core, u16 offset)
+{
+ if (core->bus->mapped_core != core)
+ bcma_host_pci_switch_core(core);
+ return ioread16(core->bus->mmio + offset);
+}
+
+static u32 bcma_host_pci_read32(struct bcma_device *core, u16 offset)
+{
+ if (core->bus->mapped_core != core)
+ bcma_host_pci_switch_core(core);
+ return ioread32(core->bus->mmio + offset);
+}
+
+static void bcma_host_pci_write8(struct bcma_device *core, u16 offset,
+ u8 value)
+{
+ if (core->bus->mapped_core != core)
+ bcma_host_pci_switch_core(core);
+ iowrite8(value, core->bus->mmio + offset);
+}
+
+static void bcma_host_pci_write16(struct bcma_device *core, u16 offset,
+ u16 value)
+{
+ if (core->bus->mapped_core != core)
+ bcma_host_pci_switch_core(core);
+ iowrite16(value, core->bus->mmio + offset);
+}
+
+static void bcma_host_pci_write32(struct bcma_device *core, u16 offset,
+ u32 value)
+{
+ if (core->bus->mapped_core != core)
+ bcma_host_pci_switch_core(core);
+ iowrite32(value, core->bus->mmio + offset);
+}
+
+#ifdef CONFIG_BCMA_BLOCKIO
+void bcma_host_pci_block_read(struct bcma_device *core, void *buffer,
+ size_t count, u16 offset, u8 reg_width)
+{
+ void __iomem *addr = core->bus->mmio + offset;
+ if (core->bus->mapped_core != core)
+ bcma_host_pci_switch_core(core);
+ switch (reg_width) {
+ case sizeof(u8):
+ ioread8_rep(addr, buffer, count);
+ break;
+ case sizeof(u16):
+ WARN_ON(count & 1);
+ ioread16_rep(addr, buffer, count >> 1);
+ break;
+ case sizeof(u32):
+ WARN_ON(count & 3);
+ ioread32_rep(addr, buffer, count >> 2);
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+
+void bcma_host_pci_block_write(struct bcma_device *core, const void *buffer,
+ size_t count, u16 offset, u8 reg_width)
+{
+ void __iomem *addr = core->bus->mmio + offset;
+ if (core->bus->mapped_core != core)
+ bcma_host_pci_switch_core(core);
+ switch (reg_width) {
+ case sizeof(u8):
+ iowrite8_rep(addr, buffer, count);
+ break;
+ case sizeof(u16):
+ WARN_ON(count & 1);
+ iowrite16_rep(addr, buffer, count >> 1);
+ break;
+ case sizeof(u32):
+ WARN_ON(count & 3);
+ iowrite32_rep(addr, buffer, count >> 2);
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+#endif
+
+static u32 bcma_host_pci_aread32(struct bcma_device *core, u16 offset)
+{
+ if (core->bus->mapped_core != core)
+ bcma_host_pci_switch_core(core);
+ return ioread32(core->bus->mmio + (1 * BCMA_CORE_SIZE) + offset);
+}
+
+static void bcma_host_pci_awrite32(struct bcma_device *core, u16 offset,
+ u32 value)
+{
+ if (core->bus->mapped_core != core)
+ bcma_host_pci_switch_core(core);
+ iowrite32(value, core->bus->mmio + (1 * BCMA_CORE_SIZE) + offset);
+}
+
+const struct bcma_host_ops bcma_host_pci_ops = {
+ .read8 = bcma_host_pci_read8,
+ .read16 = bcma_host_pci_read16,
+ .read32 = bcma_host_pci_read32,
+ .write8 = bcma_host_pci_write8,
+ .write16 = bcma_host_pci_write16,
+ .write32 = bcma_host_pci_write32,
+#ifdef CONFIG_BCMA_BLOCKIO
+ .block_read = bcma_host_pci_block_read,
+ .block_write = bcma_host_pci_block_write,
+#endif
+ .aread32 = bcma_host_pci_aread32,
+ .awrite32 = bcma_host_pci_awrite32,
+};
+
+static int bcma_host_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ struct bcma_bus *bus;
+ int err = -ENOMEM;
+ const char *name;
+ u32 val;
+
+ /* Alloc */
+ bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ goto out;
+
+ /* Basic PCI configuration */
+ err = pci_enable_device(dev);
+ if (err)
+ goto err_kfree_bus;
+
+ name = dev_name(&dev->dev);
+ if (dev->driver && dev->driver->name)
+ name = dev->driver->name;
+ err = pci_request_regions(dev, name);
+ if (err)
+ goto err_pci_disable;
+ pci_set_master(dev);
+
+ /* Disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state */
+ pci_read_config_dword(dev, 0x40, &val);
+ if ((val & 0x0000ff00) != 0)
+ pci_write_config_dword(dev, 0x40, val & 0xffff00ff);
+
+ /* SSB needed additional powering up, do we have any AMBA PCI cards? */
+ if (!pci_is_pcie(dev))
+ pr_err("PCI card detected, report problems.\n");
+
+ /* Map MMIO */
+ err = -ENOMEM;
+ bus->mmio = pci_iomap(dev, 0, ~0UL);
+ if (!bus->mmio)
+ goto err_pci_release_regions;
+
+ /* Host specific */
+ bus->host_pci = dev;
+ bus->hosttype = BCMA_HOSTTYPE_PCI;
+ bus->ops = &bcma_host_pci_ops;
+
+ /* Register */
+ err = bcma_bus_register(bus);
+ if (err)
+ goto err_pci_unmap_mmio;
+
+ pci_set_drvdata(dev, bus);
+
+out:
+ return err;
+
+err_pci_unmap_mmio:
+ pci_iounmap(dev, bus->mmio);
+err_pci_release_regions:
+ pci_release_regions(dev);
+err_pci_disable:
+ pci_disable_device(dev);
+err_kfree_bus:
+ kfree(bus);
+ return err;
+}
+
+static void bcma_host_pci_remove(struct pci_dev *dev)
+{
+ struct bcma_bus *bus = pci_get_drvdata(dev);
+
+ bcma_bus_unregister(bus);
+ pci_iounmap(dev, bus->mmio);
+ pci_release_regions(dev);
+ pci_disable_device(dev);
+ kfree(bus);
+ pci_set_drvdata(dev, NULL);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x0576) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, bcma_pci_bridge_tbl);
+
+static struct pci_driver bcma_pci_bridge_driver = {
+ .name = "bcma-pci-bridge",
+ .id_table = bcma_pci_bridge_tbl,
+ .probe = bcma_host_pci_probe,
+ .remove = bcma_host_pci_remove,
+};
+
+int __init bcma_host_pci_init(void)
+{
+ return pci_register_driver(&bcma_pci_bridge_driver);
+}
+
+void __exit bcma_host_pci_exit(void)
+{
+ pci_unregister_driver(&bcma_pci_bridge_driver);
+}
diff --git a/drivers/bcma/host_soc.c b/drivers/bcma/host_soc.c
new file mode 100644
index 0000000..3c381fb
--- /dev/null
+++ b/drivers/bcma/host_soc.c
@@ -0,0 +1,183 @@
+/*
+ * Broadcom specific AMBA
+ * System on Chip (SoC) Host
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include "scan.h"
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_soc.h>
+
+static u8 bcma_host_soc_read8(struct bcma_device *core, u16 offset)
+{
+ return readb(core->io_addr + offset);
+}
+
+static u16 bcma_host_soc_read16(struct bcma_device *core, u16 offset)
+{
+ return readw(core->io_addr + offset);
+}
+
+static u32 bcma_host_soc_read32(struct bcma_device *core, u16 offset)
+{
+ return readl(core->io_addr + offset);
+}
+
+static void bcma_host_soc_write8(struct bcma_device *core, u16 offset,
+ u8 value)
+{
+ writeb(value, core->io_addr + offset);
+}
+
+static void bcma_host_soc_write16(struct bcma_device *core, u16 offset,
+ u16 value)
+{
+ writew(value, core->io_addr + offset);
+}
+
+static void bcma_host_soc_write32(struct bcma_device *core, u16 offset,
+ u32 value)
+{
+ writel(value, core->io_addr + offset);
+}
+
+#ifdef CONFIG_BCMA_BLOCKIO
+static void bcma_host_soc_block_read(struct bcma_device *core, void *buffer,
+ size_t count, u16 offset, u8 reg_width)
+{
+ void __iomem *addr = core->io_addr + offset;
+
+ switch (reg_width) {
+ case sizeof(u8): {
+ u8 *buf = buffer;
+
+ while (count) {
+ *buf = __raw_readb(addr);
+ buf++;
+ count--;
+ }
+ break;
+ }
+ case sizeof(u16): {
+ __le16 *buf = buffer;
+
+ WARN_ON(count & 1);
+ while (count) {
+ *buf = (__force __le16)__raw_readw(addr);
+ buf++;
+ count -= 2;
+ }
+ break;
+ }
+ case sizeof(u32): {
+ __le32 *buf = buffer;
+
+ WARN_ON(count & 3);
+ while (count) {
+ *buf = (__force __le32)__raw_readl(addr);
+ buf++;
+ count -= 4;
+ }
+ break;
+ }
+ default:
+ WARN_ON(1);
+ }
+}
+
+static void bcma_host_soc_block_write(struct bcma_device *core,
+ const void *buffer,
+ size_t count, u16 offset, u8 reg_width)
+{
+ void __iomem *addr = core->io_addr + offset;
+
+ switch (reg_width) {
+ case sizeof(u8): {
+ const u8 *buf = buffer;
+
+ while (count) {
+ __raw_writeb(*buf, addr);
+ buf++;
+ count--;
+ }
+ break;
+ }
+ case sizeof(u16): {
+ const __le16 *buf = buffer;
+
+ WARN_ON(count & 1);
+ while (count) {
+ __raw_writew((__force u16)(*buf), addr);
+ buf++;
+ count -= 2;
+ }
+ break;
+ }
+ case sizeof(u32): {
+ const __le32 *buf = buffer;
+
+ WARN_ON(count & 3);
+ while (count) {
+ __raw_writel((__force u32)(*buf), addr);
+ buf++;
+ count -= 4;
+ }
+ break;
+ }
+ default:
+ WARN_ON(1);
+ }
+}
+#endif /* CONFIG_BCMA_BLOCKIO */
+
+static u32 bcma_host_soc_aread32(struct bcma_device *core, u16 offset)
+{
+ return readl(core->io_wrap + offset);
+}
+
+static void bcma_host_soc_awrite32(struct bcma_device *core, u16 offset,
+ u32 value)
+{
+ writel(value, core->io_wrap + offset);
+}
+
+const struct bcma_host_ops bcma_host_soc_ops = {
+ .read8 = bcma_host_soc_read8,
+ .read16 = bcma_host_soc_read16,
+ .read32 = bcma_host_soc_read32,
+ .write8 = bcma_host_soc_write8,
+ .write16 = bcma_host_soc_write16,
+ .write32 = bcma_host_soc_write32,
+#ifdef CONFIG_BCMA_BLOCKIO
+ .block_read = bcma_host_soc_block_read,
+ .block_write = bcma_host_soc_block_write,
+#endif
+ .aread32 = bcma_host_soc_aread32,
+ .awrite32 = bcma_host_soc_awrite32,
+};
+
+int __init bcma_host_soc_register(struct bcma_soc *soc)
+{
+ struct bcma_bus *bus = &soc->bus;
+ int err;
+
+ /* iomap only first core. We have to read some register on this core
+ * to scan the bus.
+ */
+ bus->mmio = ioremap_nocache(BCMA_ADDR_BASE, BCMA_CORE_SIZE * 1);
+ if (!bus->mmio)
+ return -ENOMEM;
+
+ /* Host specific */
+ bus->hosttype = BCMA_HOSTTYPE_SOC;
+ bus->ops = &bcma_host_soc_ops;
+
+ /* Register */
+ err = bcma_bus_early_register(bus, &soc->core_cc, &soc->core_mips);
+ if (err)
+ iounmap(bus->mmio);
+
+ return err;
+}
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
new file mode 100644
index 0000000..70c84b9
--- /dev/null
+++ b/drivers/bcma/main.c
@@ -0,0 +1,338 @@
+/*
+ * Broadcom specific AMBA
+ * Bus subsystem
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/module.h>
+#include <linux/bcma/bcma.h>
+#include <linux/slab.h>
+
+MODULE_DESCRIPTION("Broadcom's specific AMBA driver");
+MODULE_LICENSE("GPL");
+
+static int bcma_bus_match(struct device *dev, struct device_driver *drv);
+static int bcma_device_probe(struct device *dev);
+static int bcma_device_remove(struct device *dev);
+static int bcma_device_uevent(struct device *dev, struct kobj_uevent_env *env);
+
+static ssize_t manuf_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+ return sprintf(buf, "0x%03X\n", core->id.manuf);
+}
+static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+ return sprintf(buf, "0x%03X\n", core->id.id);
+}
+static ssize_t rev_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+ return sprintf(buf, "0x%02X\n", core->id.rev);
+}
+static ssize_t class_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+ return sprintf(buf, "0x%X\n", core->id.class);
+}
+static struct device_attribute bcma_device_attrs[] = {
+ __ATTR_RO(manuf),
+ __ATTR_RO(id),
+ __ATTR_RO(rev),
+ __ATTR_RO(class),
+ __ATTR_NULL,
+};
+
+static struct bus_type bcma_bus_type = {
+ .name = "bcma",
+ .match = bcma_bus_match,
+ .probe = bcma_device_probe,
+ .remove = bcma_device_remove,
+ .uevent = bcma_device_uevent,
+ .dev_attrs = bcma_device_attrs,
+};
+
+static struct bcma_device *bcma_find_core(struct bcma_bus *bus, u16 coreid)
+{
+ struct bcma_device *core;
+
+ list_for_each_entry(core, &bus->cores, list) {
+ if (core->id.id == coreid)
+ return core;
+ }
+ return NULL;
+}
+
+static void bcma_release_core_dev(struct device *dev)
+{
+ struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+ if (core->io_addr)
+ iounmap(core->io_addr);
+ if (core->io_wrap)
+ iounmap(core->io_wrap);
+ kfree(core);
+}
+
+static int bcma_register_cores(struct bcma_bus *bus)
+{
+ struct bcma_device *core;
+ int err, dev_id = 0;
+
+ list_for_each_entry(core, &bus->cores, list) {
+ /* We support that cores ourself */
+ switch (core->id.id) {
+ case BCMA_CORE_CHIPCOMMON:
+ case BCMA_CORE_PCI:
+ case BCMA_CORE_PCIE:
+ case BCMA_CORE_MIPS_74K:
+ continue;
+ }
+
+ core->dev.release = bcma_release_core_dev;
+ core->dev.bus = &bcma_bus_type;
+ dev_set_name(&core->dev, "bcma%d:%d", 0/*bus->num*/, dev_id);
+
+ switch (bus->hosttype) {
+ case BCMA_HOSTTYPE_PCI:
+ core->dev.parent = &bus->host_pci->dev;
+ core->dma_dev = &bus->host_pci->dev;
+ core->irq = bus->host_pci->irq;
+ break;
+ case BCMA_HOSTTYPE_SOC:
+ core->dev.dma_mask = &core->dev.coherent_dma_mask;
+ core->dma_dev = &core->dev;
+ break;
+ case BCMA_HOSTTYPE_SDIO:
+ break;
+ }
+
+ err = device_register(&core->dev);
+ if (err) {
+ pr_err("Could not register dev for core 0x%03X\n",
+ core->id.id);
+ continue;
+ }
+ core->dev_registered = true;
+ dev_id++;
+ }
+
+ return 0;
+}
+
+static void bcma_unregister_cores(struct bcma_bus *bus)
+{
+ struct bcma_device *core;
+
+ list_for_each_entry(core, &bus->cores, list) {
+ if (core->dev_registered)
+ device_unregister(&core->dev);
+ }
+}
+
+int bcma_bus_register(struct bcma_bus *bus)
+{
+ int err;
+ struct bcma_device *core;
+
+ /* Scan for devices (cores) */
+ err = bcma_bus_scan(bus);
+ if (err) {
+ pr_err("Failed to scan: %d\n", err);
+ return -1;
+ }
+
+ /* Init CC core */
+ core = bcma_find_core(bus, BCMA_CORE_CHIPCOMMON);
+ if (core) {
+ bus->drv_cc.core = core;
+ bcma_core_chipcommon_init(&bus->drv_cc);
+ }
+
+ /* Init MIPS core */
+ core = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
+ if (core) {
+ bus->drv_mips.core = core;
+ bcma_core_mips_init(&bus->drv_mips);
+ }
+
+ /* Init PCIE core */
+ core = bcma_find_core(bus, BCMA_CORE_PCIE);
+ if (core) {
+ bus->drv_pci.core = core;
+ bcma_core_pci_init(&bus->drv_pci);
+ }
+
+ /* Try to get SPROM */
+ err = bcma_sprom_get(bus);
+ if (err == -ENOENT) {
+ pr_err("No SPROM available\n");
+ } else if (err) {
+ pr_err("Failed to get SPROM: %d\n", err);
+ return -ENOENT;
+ }
+
+ /* Register found cores */
+ bcma_register_cores(bus);
+
+ pr_info("Bus registered\n");
+
+ return 0;
+}
+
+void bcma_bus_unregister(struct bcma_bus *bus)
+{
+ bcma_unregister_cores(bus);
+}
+
+int __init bcma_bus_early_register(struct bcma_bus *bus,
+ struct bcma_device *core_cc,
+ struct bcma_device *core_mips)
+{
+ int err;
+ struct bcma_device *core;
+ struct bcma_device_id match;
+
+ bcma_init_bus(bus);
+
+ match.manuf = BCMA_MANUF_BCM;
+ match.id = BCMA_CORE_CHIPCOMMON;
+ match.class = BCMA_CL_SIM;
+ match.rev = BCMA_ANY_REV;
+
+ /* Scan for chip common core */
+ err = bcma_bus_scan_early(bus, &match, core_cc);
+ if (err) {
+ pr_err("Failed to scan for common core: %d\n", err);
+ return -1;
+ }
+
+ match.manuf = BCMA_MANUF_MIPS;
+ match.id = BCMA_CORE_MIPS_74K;
+ match.class = BCMA_CL_SIM;
+ match.rev = BCMA_ANY_REV;
+
+ /* Scan for mips core */
+ err = bcma_bus_scan_early(bus, &match, core_mips);
+ if (err) {
+ pr_err("Failed to scan for mips core: %d\n", err);
+ return -1;
+ }
+
+ /* Init CC core */
+ core = bcma_find_core(bus, BCMA_CORE_CHIPCOMMON);
+ if (core) {
+ bus->drv_cc.core = core;
+ bcma_core_chipcommon_init(&bus->drv_cc);
+ }
+
+ /* Init MIPS core */
+ core = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
+ if (core) {
+ bus->drv_mips.core = core;
+ bcma_core_mips_init(&bus->drv_mips);
+ }
+
+ pr_info("Early bus registered\n");
+
+ return 0;
+}
+
+int __bcma_driver_register(struct bcma_driver *drv, struct module *owner)
+{
+ drv->drv.name = drv->name;
+ drv->drv.bus = &bcma_bus_type;
+ drv->drv.owner = owner;
+
+ return driver_register(&drv->drv);
+}
+EXPORT_SYMBOL_GPL(__bcma_driver_register);
+
+void bcma_driver_unregister(struct bcma_driver *drv)
+{
+ driver_unregister(&drv->drv);
+}
+EXPORT_SYMBOL_GPL(bcma_driver_unregister);
+
+static int bcma_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+ struct bcma_driver *adrv = container_of(drv, struct bcma_driver, drv);
+ const struct bcma_device_id *cid = &core->id;
+ const struct bcma_device_id *did;
+
+ for (did = adrv->id_table; did->manuf || did->id || did->rev; did++) {
+ if ((did->manuf == cid->manuf || did->manuf == BCMA_ANY_MANUF) &&
+ (did->id == cid->id || did->id == BCMA_ANY_ID) &&
+ (did->rev == cid->rev || did->rev == BCMA_ANY_REV) &&
+ (did->class == cid->class || did->class == BCMA_ANY_CLASS))
+ return 1;
+ }
+ return 0;
+}
+
+static int bcma_device_probe(struct device *dev)
+{
+ struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+ struct bcma_driver *adrv = container_of(dev->driver, struct bcma_driver,
+ drv);
+ int err = 0;
+
+ if (adrv->probe)
+ err = adrv->probe(core);
+
+ return err;
+}
+
+static int bcma_device_remove(struct device *dev)
+{
+ struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+ struct bcma_driver *adrv = container_of(dev->driver, struct bcma_driver,
+ drv);
+
+ if (adrv->remove)
+ adrv->remove(core);
+
+ return 0;
+}
+
+static int bcma_device_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+
+ return add_uevent_var(env,
+ "MODALIAS=bcma:m%04Xid%04Xrev%02Xcl%02X",
+ core->id.manuf, core->id.id,
+ core->id.rev, core->id.class);
+}
+
+static int __init bcma_modinit(void)
+{
+ int err;
+
+ err = bus_register(&bcma_bus_type);
+ if (err)
+ return err;
+
+#ifdef CONFIG_BCMA_HOST_PCI
+ err = bcma_host_pci_init();
+ if (err) {
+ pr_err("PCI host initialization failed\n");
+ err = 0;
+ }
+#endif
+
+ return err;
+}
+fs_initcall(bcma_modinit);
+
+static void __exit bcma_modexit(void)
+{
+#ifdef CONFIG_BCMA_HOST_PCI
+ bcma_host_pci_exit();
+#endif
+ bus_unregister(&bcma_bus_type);
+}
+module_exit(bcma_modexit)
diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c
new file mode 100644
index 0000000..cad9948
--- /dev/null
+++ b/drivers/bcma/scan.c
@@ -0,0 +1,486 @@
+/*
+ * Broadcom specific AMBA
+ * Bus scanning
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "scan.h"
+#include "bcma_private.h"
+
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_regs.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+struct bcma_device_id_name {
+ u16 id;
+ const char *name;
+};
+struct bcma_device_id_name bcma_device_names[] = {
+ { BCMA_CORE_OOB_ROUTER, "OOB Router" },
+ { BCMA_CORE_INVALID, "Invalid" },
+ { BCMA_CORE_CHIPCOMMON, "ChipCommon" },
+ { BCMA_CORE_ILINE20, "ILine 20" },
+ { BCMA_CORE_SRAM, "SRAM" },
+ { BCMA_CORE_SDRAM, "SDRAM" },
+ { BCMA_CORE_PCI, "PCI" },
+ { BCMA_CORE_MIPS, "MIPS" },
+ { BCMA_CORE_ETHERNET, "Fast Ethernet" },
+ { BCMA_CORE_V90, "V90" },
+ { BCMA_CORE_USB11_HOSTDEV, "USB 1.1 Hostdev" },
+ { BCMA_CORE_ADSL, "ADSL" },
+ { BCMA_CORE_ILINE100, "ILine 100" },
+ { BCMA_CORE_IPSEC, "IPSEC" },
+ { BCMA_CORE_UTOPIA, "UTOPIA" },
+ { BCMA_CORE_PCMCIA, "PCMCIA" },
+ { BCMA_CORE_INTERNAL_MEM, "Internal Memory" },
+ { BCMA_CORE_MEMC_SDRAM, "MEMC SDRAM" },
+ { BCMA_CORE_OFDM, "OFDM" },
+ { BCMA_CORE_EXTIF, "EXTIF" },
+ { BCMA_CORE_80211, "IEEE 802.11" },
+ { BCMA_CORE_PHY_A, "PHY A" },
+ { BCMA_CORE_PHY_B, "PHY B" },
+ { BCMA_CORE_PHY_G, "PHY G" },
+ { BCMA_CORE_MIPS_3302, "MIPS 3302" },
+ { BCMA_CORE_USB11_HOST, "USB 1.1 Host" },
+ { BCMA_CORE_USB11_DEV, "USB 1.1 Device" },
+ { BCMA_CORE_USB20_HOST, "USB 2.0 Host" },
+ { BCMA_CORE_USB20_DEV, "USB 2.0 Device" },
+ { BCMA_CORE_SDIO_HOST, "SDIO Host" },
+ { BCMA_CORE_ROBOSWITCH, "Roboswitch" },
+ { BCMA_CORE_PARA_ATA, "PATA" },
+ { BCMA_CORE_SATA_XORDMA, "SATA XOR-DMA" },
+ { BCMA_CORE_ETHERNET_GBIT, "GBit Ethernet" },
+ { BCMA_CORE_PCIE, "PCIe" },
+ { BCMA_CORE_PHY_N, "PHY N" },
+ { BCMA_CORE_SRAM_CTL, "SRAM Controller" },
+ { BCMA_CORE_MINI_MACPHY, "Mini MACPHY" },
+ { BCMA_CORE_ARM_1176, "ARM 1176" },
+ { BCMA_CORE_ARM_7TDMI, "ARM 7TDMI" },
+ { BCMA_CORE_PHY_LP, "PHY LP" },
+ { BCMA_CORE_PMU, "PMU" },
+ { BCMA_CORE_PHY_SSN, "PHY SSN" },
+ { BCMA_CORE_SDIO_DEV, "SDIO Device" },
+ { BCMA_CORE_ARM_CM3, "ARM CM3" },
+ { BCMA_CORE_PHY_HT, "PHY HT" },
+ { BCMA_CORE_MIPS_74K, "MIPS 74K" },
+ { BCMA_CORE_MAC_GBIT, "GBit MAC" },
+ { BCMA_CORE_DDR12_MEM_CTL, "DDR1/DDR2 Memory Controller" },
+ { BCMA_CORE_PCIE_RC, "PCIe Root Complex" },
+ { BCMA_CORE_OCP_OCP_BRIDGE, "OCP to OCP Bridge" },
+ { BCMA_CORE_SHARED_COMMON, "Common Shared" },
+ { BCMA_CORE_OCP_AHB_BRIDGE, "OCP to AHB Bridge" },
+ { BCMA_CORE_SPI_HOST, "SPI Host" },
+ { BCMA_CORE_I2S, "I2S" },
+ { BCMA_CORE_SDR_DDR1_MEM_CTL, "SDR/DDR1 Memory Controller" },
+ { BCMA_CORE_SHIM, "SHIM" },
+ { BCMA_CORE_DEFAULT, "Default" },
+};
+const char *bcma_device_name(struct bcma_device_id *id)
+{
+ int i;
+
+ if (id->manuf == BCMA_MANUF_BCM) {
+ for (i = 0; i < ARRAY_SIZE(bcma_device_names); i++) {
+ if (bcma_device_names[i].id == id->id)
+ return bcma_device_names[i].name;
+ }
+ }
+ return "UNKNOWN";
+}
+
+static u32 bcma_scan_read32(struct bcma_bus *bus, u8 current_coreidx,
+ u16 offset)
+{
+ return readl(bus->mmio + offset);
+}
+
+static void bcma_scan_switch_core(struct bcma_bus *bus, u32 addr)
+{
+ if (bus->hosttype == BCMA_HOSTTYPE_PCI)
+ pci_write_config_dword(bus->host_pci, BCMA_PCI_BAR0_WIN,
+ addr);
+}
+
+static u32 bcma_erom_get_ent(struct bcma_bus *bus, u32 **eromptr)
+{
+ u32 ent = readl(*eromptr);
+ (*eromptr)++;
+ return ent;
+}
+
+static void bcma_erom_push_ent(u32 **eromptr)
+{
+ (*eromptr)--;
+}
+
+static s32 bcma_erom_get_ci(struct bcma_bus *bus, u32 **eromptr)
+{
+ u32 ent = bcma_erom_get_ent(bus, eromptr);
+ if (!(ent & SCAN_ER_VALID))
+ return -ENOENT;
+ if ((ent & SCAN_ER_TAG) != SCAN_ER_TAG_CI)
+ return -ENOENT;
+ return ent;
+}
+
+static bool bcma_erom_is_end(struct bcma_bus *bus, u32 **eromptr)
+{
+ u32 ent = bcma_erom_get_ent(bus, eromptr);
+ bcma_erom_push_ent(eromptr);
+ return (ent == (SCAN_ER_TAG_END | SCAN_ER_VALID));
+}
+
+static bool bcma_erom_is_bridge(struct bcma_bus *bus, u32 **eromptr)
+{
+ u32 ent = bcma_erom_get_ent(bus, eromptr);
+ bcma_erom_push_ent(eromptr);
+ return (((ent & SCAN_ER_VALID)) &&
+ ((ent & SCAN_ER_TAGX) == SCAN_ER_TAG_ADDR) &&
+ ((ent & SCAN_ADDR_TYPE) == SCAN_ADDR_TYPE_BRIDGE));
+}
+
+static void bcma_erom_skip_component(struct bcma_bus *bus, u32 **eromptr)
+{
+ u32 ent;
+ while (1) {
+ ent = bcma_erom_get_ent(bus, eromptr);
+ if ((ent & SCAN_ER_VALID) &&
+ ((ent & SCAN_ER_TAG) == SCAN_ER_TAG_CI))
+ break;
+ if (ent == (SCAN_ER_TAG_END | SCAN_ER_VALID))
+ break;
+ }
+ bcma_erom_push_ent(eromptr);
+}
+
+static s32 bcma_erom_get_mst_port(struct bcma_bus *bus, u32 **eromptr)
+{
+ u32 ent = bcma_erom_get_ent(bus, eromptr);
+ if (!(ent & SCAN_ER_VALID))
+ return -ENOENT;
+ if ((ent & SCAN_ER_TAG) != SCAN_ER_TAG_MP)
+ return -ENOENT;
+ return ent;
+}
+
+static s32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 **eromptr,
+ u32 type, u8 port)
+{
+ u32 addrl, addrh, sizel, sizeh = 0;
+ u32 size;
+
+ u32 ent = bcma_erom_get_ent(bus, eromptr);
+ if ((!(ent & SCAN_ER_VALID)) ||
+ ((ent & SCAN_ER_TAGX) != SCAN_ER_TAG_ADDR) ||
+ ((ent & SCAN_ADDR_TYPE) != type) ||
+ (((ent & SCAN_ADDR_PORT) >> SCAN_ADDR_PORT_SHIFT) != port)) {
+ bcma_erom_push_ent(eromptr);
+ return -EINVAL;
+ }
+
+ addrl = ent & SCAN_ADDR_ADDR;
+ if (ent & SCAN_ADDR_AG32)
+ addrh = bcma_erom_get_ent(bus, eromptr);
+ else
+ addrh = 0;
+
+ if ((ent & SCAN_ADDR_SZ) == SCAN_ADDR_SZ_SZD) {
+ size = bcma_erom_get_ent(bus, eromptr);
+ sizel = size & SCAN_SIZE_SZ;
+ if (size & SCAN_SIZE_SG32)
+ sizeh = bcma_erom_get_ent(bus, eromptr);
+ } else
+ sizel = SCAN_ADDR_SZ_BASE <<
+ ((ent & SCAN_ADDR_SZ) >> SCAN_ADDR_SZ_SHIFT);
+
+ return addrl;
+}
+
+static struct bcma_device *bcma_find_core_by_index(struct bcma_bus *bus,
+ u16 index)
+{
+ struct bcma_device *core;
+
+ list_for_each_entry(core, &bus->cores, list) {
+ if (core->core_index == index)
+ return core;
+ }
+ return NULL;
+}
+
+static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
+ struct bcma_device_id *match, int core_num,
+ struct bcma_device *core)
+{
+ s32 tmp;
+ u8 i, j;
+ s32 cia, cib;
+ u8 ports[2], wrappers[2];
+
+ /* get CIs */
+ cia = bcma_erom_get_ci(bus, eromptr);
+ if (cia < 0) {
+ bcma_erom_push_ent(eromptr);
+ if (bcma_erom_is_end(bus, eromptr))
+ return -ESPIPE;
+ return -EILSEQ;
+ }
+ cib = bcma_erom_get_ci(bus, eromptr);
+ if (cib < 0)
+ return -EILSEQ;
+
+ /* parse CIs */
+ core->id.class = (cia & SCAN_CIA_CLASS) >> SCAN_CIA_CLASS_SHIFT;
+ core->id.id = (cia & SCAN_CIA_ID) >> SCAN_CIA_ID_SHIFT;
+ core->id.manuf = (cia & SCAN_CIA_MANUF) >> SCAN_CIA_MANUF_SHIFT;
+ ports[0] = (cib & SCAN_CIB_NMP) >> SCAN_CIB_NMP_SHIFT;
+ ports[1] = (cib & SCAN_CIB_NSP) >> SCAN_CIB_NSP_SHIFT;
+ wrappers[0] = (cib & SCAN_CIB_NMW) >> SCAN_CIB_NMW_SHIFT;
+ wrappers[1] = (cib & SCAN_CIB_NSW) >> SCAN_CIB_NSW_SHIFT;
+ core->id.rev = (cib & SCAN_CIB_REV) >> SCAN_CIB_REV_SHIFT;
+
+ if (((core->id.manuf == BCMA_MANUF_ARM) &&
+ (core->id.id == 0xFFF)) ||
+ (ports[1] == 0)) {
+ bcma_erom_skip_component(bus, eromptr);
+ return -ENXIO;
+ }
+
+ /* check if component is a core at all */
+ if (wrappers[0] + wrappers[1] == 0) {
+ /* we could save addrl of the router
+ if (cid == BCMA_CORE_OOB_ROUTER)
+ */
+ bcma_erom_skip_component(bus, eromptr);
+ return -ENXIO;
+ }
+
+ if (bcma_erom_is_bridge(bus, eromptr)) {
+ bcma_erom_skip_component(bus, eromptr);
+ return -ENXIO;
+ }
+
+ if (bcma_find_core_by_index(bus, core_num)) {
+ bcma_erom_skip_component(bus, eromptr);
+ return -ENODEV;
+ }
+
+ if (match && ((match->manuf != BCMA_ANY_MANUF &&
+ match->manuf != core->id.manuf) ||
+ (match->id != BCMA_ANY_ID && match->id != core->id.id) ||
+ (match->rev != BCMA_ANY_REV && match->rev != core->id.rev) ||
+ (match->class != BCMA_ANY_CLASS && match->class != core->id.class)
+ )) {
+ bcma_erom_skip_component(bus, eromptr);
+ return -ENODEV;
+ }
+
+ /* get & parse master ports */
+ for (i = 0; i < ports[0]; i++) {
+ s32 mst_port_d = bcma_erom_get_mst_port(bus, eromptr);
+ if (mst_port_d < 0)
+ return -EILSEQ;
+ }
+
+ /* get & parse slave ports */
+ for (i = 0; i < ports[1]; i++) {
+ for (j = 0; ; j++) {
+ tmp = bcma_erom_get_addr_desc(bus, eromptr,
+ SCAN_ADDR_TYPE_SLAVE, i);
+ if (tmp < 0) {
+ /* no more entries for port _i_ */
+ /* pr_debug("erom: slave port %d "
+ * "has %d descriptors\n", i, j); */
+ break;
+ } else {
+ if (i == 0 && j == 0)
+ core->addr = tmp;
+ }
+ }
+ }
+
+ /* get & parse master wrappers */
+ for (i = 0; i < wrappers[0]; i++) {
+ for (j = 0; ; j++) {
+ tmp = bcma_erom_get_addr_desc(bus, eromptr,
+ SCAN_ADDR_TYPE_MWRAP, i);
+ if (tmp < 0) {
+ /* no more entries for port _i_ */
+ /* pr_debug("erom: master wrapper %d "
+ * "has %d descriptors\n", i, j); */
+ break;
+ } else {
+ if (i == 0 && j == 0)
+ core->wrap = tmp;
+ }
+ }
+ }
+
+ /* get & parse slave wrappers */
+ for (i = 0; i < wrappers[1]; i++) {
+ u8 hack = (ports[1] == 1) ? 0 : 1;
+ for (j = 0; ; j++) {
+ tmp = bcma_erom_get_addr_desc(bus, eromptr,
+ SCAN_ADDR_TYPE_SWRAP, i + hack);
+ if (tmp < 0) {
+ /* no more entries for port _i_ */
+ /* pr_debug("erom: master wrapper %d "
+ * has %d descriptors\n", i, j); */
+ break;
+ } else {
+ if (wrappers[0] == 0 && !i && !j)
+ core->wrap = tmp;
+ }
+ }
+ }
+ if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
+ core->io_addr = ioremap_nocache(core->addr, BCMA_CORE_SIZE);
+ if (!core->io_addr)
+ return -ENOMEM;
+ core->io_wrap = ioremap_nocache(core->wrap, BCMA_CORE_SIZE);
+ if (!core->io_wrap) {
+ iounmap(core->io_addr);
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+void bcma_init_bus(struct bcma_bus *bus)
+{
+ s32 tmp;
+
+ if (bus->init_done)
+ return;
+
+ INIT_LIST_HEAD(&bus->cores);
+ bus->nr_cores = 0;
+
+ bcma_scan_switch_core(bus, BCMA_ADDR_BASE);
+
+ tmp = bcma_scan_read32(bus, 0, BCMA_CC_ID);
+ bus->chipinfo.id = (tmp & BCMA_CC_ID_ID) >> BCMA_CC_ID_ID_SHIFT;
+ bus->chipinfo.rev = (tmp & BCMA_CC_ID_REV) >> BCMA_CC_ID_REV_SHIFT;
+ bus->chipinfo.pkg = (tmp & BCMA_CC_ID_PKG) >> BCMA_CC_ID_PKG_SHIFT;
+ bus->init_done = true;
+}
+
+int bcma_bus_scan(struct bcma_bus *bus)
+{
+ u32 erombase;
+ u32 __iomem *eromptr, *eromend;
+
+ int err, core_num = 0;
+
+ bcma_init_bus(bus);
+
+ erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM);
+ if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
+ eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE);
+ if (!eromptr)
+ return -ENOMEM;
+ } else {
+ eromptr = bus->mmio;
+ }
+
+ eromend = eromptr + BCMA_CORE_SIZE / sizeof(u32);
+
+ bcma_scan_switch_core(bus, erombase);
+
+ while (eromptr < eromend) {
+ struct bcma_device *core = kzalloc(sizeof(*core), GFP_KERNEL);
+ if (!core)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&core->list);
+ core->bus = bus;
+
+ err = bcma_get_next_core(bus, &eromptr, NULL, core_num, core);
+ if (err == -ENODEV) {
+ core_num++;
+ continue;
+ } else if (err == -ENXIO)
+ continue;
+ else if (err == -ESPIPE)
+ break;
+ else if (err < 0)
+ return err;
+
+ core->core_index = core_num++;
+ bus->nr_cores++;
+
+ pr_info("Core %d found: %s "
+ "(manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
+ core->core_index, bcma_device_name(&core->id),
+ core->id.manuf, core->id.id, core->id.rev,
+ core->id.class);
+
+ list_add(&core->list, &bus->cores);
+ }
+
+ if (bus->hosttype == BCMA_HOSTTYPE_SOC)
+ iounmap(eromptr);
+
+ return 0;
+}
+
+int __init bcma_bus_scan_early(struct bcma_bus *bus,
+ struct bcma_device_id *match,
+ struct bcma_device *core)
+{
+ u32 erombase;
+ u32 __iomem *eromptr, *eromend;
+
+ int err = -ENODEV;
+ int core_num = 0;
+
+ erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM);
+ if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
+ eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE);
+ if (!eromptr)
+ return -ENOMEM;
+ } else {
+ eromptr = bus->mmio;
+ }
+
+ eromend = eromptr + BCMA_CORE_SIZE / sizeof(u32);
+
+ bcma_scan_switch_core(bus, erombase);
+
+ while (eromptr < eromend) {
+ memset(core, 0, sizeof(*core));
+ INIT_LIST_HEAD(&core->list);
+ core->bus = bus;
+
+ err = bcma_get_next_core(bus, &eromptr, match, core_num, core);
+ if (err == -ENODEV) {
+ core_num++;
+ continue;
+ } else if (err == -ENXIO)
+ continue;
+ else if (err == -ESPIPE)
+ break;
+ else if (err < 0)
+ return err;
+
+ core->core_index = core_num++;
+ bus->nr_cores++;
+ pr_info("Core %d found: %s "
+ "(manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
+ core->core_index, bcma_device_name(&core->id),
+ core->id.manuf, core->id.id, core->id.rev,
+ core->id.class);
+
+ list_add(&core->list, &bus->cores);
+ err = 0;
+ break;
+ }
+
+ if (bus->hosttype == BCMA_HOSTTYPE_SOC)
+ iounmap(eromptr);
+
+ return err;
+}
diff --git a/drivers/bcma/scan.h b/drivers/bcma/scan.h
new file mode 100644
index 0000000..113e6a6
--- /dev/null
+++ b/drivers/bcma/scan.h
@@ -0,0 +1,56 @@
+#ifndef BCMA_SCAN_H_
+#define BCMA_SCAN_H_
+
+#define BCMA_ADDR_BASE 0x18000000
+#define BCMA_WRAP_BASE 0x18100000
+
+#define SCAN_ER_VALID 0x00000001
+#define SCAN_ER_TAGX 0x00000006 /* we have to ignore 0x8 bit when checking tag for SCAN_ER_TAG_ADDR */
+#define SCAN_ER_TAG 0x0000000E
+#define SCAN_ER_TAG_CI 0x00000000
+#define SCAN_ER_TAG_MP 0x00000002
+#define SCAN_ER_TAG_ADDR 0x00000004
+#define SCAN_ER_TAG_END 0x0000000E
+#define SCAN_ER_BAD 0xFFFFFFFF
+
+#define SCAN_CIA_CLASS 0x000000F0
+#define SCAN_CIA_CLASS_SHIFT 4
+#define SCAN_CIA_ID 0x000FFF00
+#define SCAN_CIA_ID_SHIFT 8
+#define SCAN_CIA_MANUF 0xFFF00000
+#define SCAN_CIA_MANUF_SHIFT 20
+
+#define SCAN_CIB_NMP 0x000001F0
+#define SCAN_CIB_NMP_SHIFT 4
+#define SCAN_CIB_NSP 0x00003E00
+#define SCAN_CIB_NSP_SHIFT 9
+#define SCAN_CIB_NMW 0x0007C000
+#define SCAN_CIB_NMW_SHIFT 14
+#define SCAN_CIB_NSW 0x00F80000
+#define SCAN_CIB_NSW_SHIFT 17
+#define SCAN_CIB_REV 0xFF000000
+#define SCAN_CIB_REV_SHIFT 24
+
+#define SCAN_ADDR_AG32 0x00000008
+#define SCAN_ADDR_SZ 0x00000030
+#define SCAN_ADDR_SZ_SHIFT 4
+#define SCAN_ADDR_SZ_4K 0x00000000
+#define SCAN_ADDR_SZ_8K 0x00000010
+#define SCAN_ADDR_SZ_16K 0x00000020
+#define SCAN_ADDR_SZ_SZD 0x00000030
+#define SCAN_ADDR_TYPE 0x000000C0
+#define SCAN_ADDR_TYPE_SLAVE 0x00000000
+#define SCAN_ADDR_TYPE_BRIDGE 0x00000040
+#define SCAN_ADDR_TYPE_SWRAP 0x00000080
+#define SCAN_ADDR_TYPE_MWRAP 0x000000C0
+#define SCAN_ADDR_PORT 0x00000F00
+#define SCAN_ADDR_PORT_SHIFT 8
+#define SCAN_ADDR_ADDR 0xFFFFF000
+
+#define SCAN_ADDR_SZ_BASE 0x00001000 /* 4KB */
+
+#define SCAN_SIZE_SZ_ALIGN 0x00000FFF
+#define SCAN_SIZE_SZ 0xFFFFF000
+#define SCAN_SIZE_SG32 0x00000008
+
+#endif /* BCMA_SCAN_H_ */
diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
new file mode 100644
index 0000000..d729239
--- /dev/null
+++ b/drivers/bcma/sprom.c
@@ -0,0 +1,186 @@
+/*
+ * Broadcom specific AMBA
+ * SPROM reading
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_regs.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+#define SPOFF(offset) ((offset) / sizeof(u16))
+
+/**************************************************
+ * R/W ops.
+ **************************************************/
+
+static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom)
+{
+ int i;
+ for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++)
+ sprom[i] = bcma_read16(bus->drv_cc.core,
+ offset + (i * 2));
+}
+
+/**************************************************
+ * Validation.
+ **************************************************/
+
+static inline u8 bcma_crc8(u8 crc, u8 data)
+{
+ /* Polynomial: x^8 + x^7 + x^6 + x^4 + x^2 + 1 */
+ static const u8 t[] = {
+ 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
+ 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
+ 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
+ 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
+ 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
+ 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
+ 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
+ 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
+ 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
+ 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
+ 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
+ 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
+ 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
+ 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
+ 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
+ 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
+ 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
+ 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
+ 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
+ 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
+ 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
+ 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
+ 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
+ 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
+ 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
+ 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
+ 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
+ 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
+ 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
+ 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
+ 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
+ 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F,
+ };
+ return t[crc ^ data];
+}
+
+static u8 bcma_sprom_crc(const u16 *sprom)
+{
+ int word;
+ u8 crc = 0xFF;
+
+ for (word = 0; word < SSB_SPROMSIZE_WORDS_R4 - 1; word++) {
+ crc = bcma_crc8(crc, sprom[word] & 0x00FF);
+ crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8);
+ }
+ crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF);
+ crc ^= 0xFF;
+
+ return crc;
+}
+
+static int bcma_sprom_check_crc(const u16 *sprom)
+{
+ u8 crc;
+ u8 expected_crc;
+ u16 tmp;
+
+ crc = bcma_sprom_crc(sprom);
+ tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC;
+ expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
+ if (crc != expected_crc)
+ return -EPROTO;
+
+ return 0;
+}
+
+static int bcma_sprom_valid(const u16 *sprom)
+{
+ u16 revision;
+ int err;
+
+ err = bcma_sprom_check_crc(sprom);
+ if (err)
+ return err;
+
+ revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV;
+ if (revision != 8 && revision != 9) {
+ pr_err("Unsupported SPROM revision: %d\n", revision);
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+/**************************************************
+ * SPROM extraction.
+ **************************************************/
+
+static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
+{
+ u16 v;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
+ *(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
+ }
+
+ bus->sprom.board_rev = sprom[SPOFF(SSB_SPROM8_BOARDREV)];
+
+ bus->sprom.boardflags_lo = sprom[SPOFF(SSB_SPROM8_BFLLO)];
+ bus->sprom.boardflags_hi = sprom[SPOFF(SSB_SPROM8_BFLHI)];
+ bus->sprom.boardflags2_lo = sprom[SPOFF(SSB_SPROM8_BFL2LO)];
+ bus->sprom.boardflags2_hi = sprom[SPOFF(SSB_SPROM8_BFL2HI)];
+
+ bus->sprom.country_code = sprom[SPOFF(SSB_SPROM8_CCODE)];
+}
+
+int bcma_sprom_get(struct bcma_bus *bus)
+{
+ u16 offset;
+ u16 *sprom;
+ int err = 0;
+
+ if (!bus->drv_cc.core)
+ return -EOPNOTSUPP;
+
+ if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM))
+ return -ENOENT;
+
+ sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
+ GFP_KERNEL);
+ if (!sprom)
+ return -ENOMEM;
+
+ if (bus->chipinfo.id == 0x4331)
+ bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
+
+ /* Most cards have SPROM moved by additional offset 0x30 (48 dwords).
+ * According to brcm80211 this applies to cards with PCIe rev >= 6
+ * TODO: understand this condition and use it */
+ offset = (bus->chipinfo.id == 0x4331) ? BCMA_CC_SPROM :
+ BCMA_CC_SPROM_PCIE6;
+ bcma_sprom_read(bus, offset, sprom);
+
+ if (bus->chipinfo.id == 0x4331)
+ bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
+
+ err = bcma_sprom_valid(sprom);
+ if (err)
+ goto out;
+
+ bcma_sprom_extract_r8(bus, sprom);
+
+out:
+ kfree(sprom);
+ return err;
+}
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
new file mode 100644
index 0000000..f4460f4
--- /dev/null
+++ b/drivers/bluetooth/Makefile
@@ -0,0 +1,31 @@
+#
+# Makefile for the Linux Bluetooth HCI device drivers.
+#
+
+obj-$(CONFIG_BT_HCIVHCI) += hci_vhci.o
+obj-$(CONFIG_BT_HCIUART) += hci_uart.o
+obj-$(CONFIG_BT_HCIBCM203X) += bcm203x.o
+obj-$(CONFIG_BT_HCIBPA10X) += bpa10x.o
+obj-$(CONFIG_BT_HCIBFUSB) += bfusb.o
+obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o
+obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o
+obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o
+obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o
+
+obj-$(CONFIG_BT_HCIBTUSB) += btusb.o
+obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o
+
+obj-$(CONFIG_BT_ATH3K) += ath3k.o
+obj-$(CONFIG_BT_MRVL) += btmrvl.o
+obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
+obj-$(CONFIG_BT_WILINK) += btwilink.o
+
+btmrvl-y := btmrvl_main.o
+btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
+
+hci_uart-y := hci_ldisc.o
+hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o
+hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o
+hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o
+hci_uart-$(CONFIG_BT_HCIUART_ATH3K) += hci_ath.o
+hci_uart-objs := $(hci_uart-y)
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
new file mode 100644
index 0000000..1622772
--- /dev/null
+++ b/drivers/bluetooth/ath3k.c
@@ -0,0 +1,451 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ *
+ * 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
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/usb.h>
+#include <net/bluetooth/bluetooth.h>
+
+#define VERSION "1.0"
+#define ATH3K_FIRMWARE "ath3k-1.fw"
+
+#define ATH3K_DNLOAD 0x01
+#define ATH3K_GETSTATE 0x05
+#define ATH3K_SET_NORMAL_MODE 0x07
+#define ATH3K_GETVERSION 0x09
+#define USB_REG_SWITCH_VID_PID 0x0a
+
+#define ATH3K_MODE_MASK 0x3F
+#define ATH3K_NORMAL_MODE 0x0E
+
+#define ATH3K_PATCH_UPDATE 0x80
+#define ATH3K_SYSCFG_UPDATE 0x40
+
+#define ATH3K_XTAL_FREQ_26M 0x00
+#define ATH3K_XTAL_FREQ_40M 0x01
+#define ATH3K_XTAL_FREQ_19P2 0x02
+#define ATH3K_NAME_LEN 0xFF
+
+struct ath3k_version {
+ unsigned int rom_version;
+ unsigned int build_version;
+ unsigned int ram_version;
+ unsigned char ref_clock;
+ unsigned char reserved[0x07];
+};
+
+static struct usb_device_id ath3k_table[] = {
+ /* Atheros AR3011 */
+ { USB_DEVICE(0x0CF3, 0x3000) },
+
+ /* Atheros AR3011 with sflash firmware*/
+ { USB_DEVICE(0x0CF3, 0x3002) },
+ { USB_DEVICE(0x13d3, 0x3304) },
+ { USB_DEVICE(0x0930, 0x0215) },
+
+ /* Atheros AR9285 Malbec with sflash firmware */
+ { USB_DEVICE(0x03F0, 0x311D) },
+
+ /* Atheros AR3012 with sflash firmware*/
+ { USB_DEVICE(0x0CF3, 0x3004) },
+
+ /* Atheros AR5BBU12 with sflash firmware */
+ { USB_DEVICE(0x0489, 0xE02C) },
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ath3k_table);
+
+#define BTUSB_ATH3012 0x80
+/* This table is to load patch and sysconfig files
+ * for AR3012 */
+static struct usb_device_id ath3k_blist_tbl[] = {
+
+ /* Atheros AR3012 with sflash firmware*/
+ { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
+
+ { } /* Terminating entry */
+};
+
+#define USB_REQ_DFU_DNLOAD 1
+#define BULK_SIZE 4096
+#define FW_HDR_SIZE 20
+
+static int ath3k_load_firmware(struct usb_device *udev,
+ const struct firmware *firmware)
+{
+ u8 *send_buf;
+ int err, pipe, len, size, sent = 0;
+ int count = firmware->size;
+
+ BT_DBG("udev %p", udev);
+
+ pipe = usb_sndctrlpipe(udev, 0);
+
+ send_buf = kmalloc(BULK_SIZE, GFP_KERNEL);
+ if (!send_buf) {
+ BT_ERR("Can't allocate memory chunk for firmware");
+ return -ENOMEM;
+ }
+
+ memcpy(send_buf, firmware->data, 20);
+ if ((err = usb_control_msg(udev, pipe,
+ USB_REQ_DFU_DNLOAD,
+ USB_TYPE_VENDOR, 0, 0,
+ send_buf, 20, USB_CTRL_SET_TIMEOUT)) < 0) {
+ BT_ERR("Can't change to loading configuration err");
+ goto error;
+ }
+ sent += 20;
+ count -= 20;
+
+ while (count) {
+ size = min_t(uint, count, BULK_SIZE);
+ pipe = usb_sndbulkpipe(udev, 0x02);
+ memcpy(send_buf, firmware->data + sent, size);
+
+ err = usb_bulk_msg(udev, pipe, send_buf, size,
+ &len, 3000);
+
+ if (err || (len != size)) {
+ BT_ERR("Error in firmware loading err = %d,"
+ "len = %d, size = %d", err, len, size);
+ goto error;
+ }
+
+ sent += size;
+ count -= size;
+ }
+
+error:
+ kfree(send_buf);
+ return err;
+}
+
+static int ath3k_get_state(struct usb_device *udev, unsigned char *state)
+{
+ int pipe = 0;
+
+ pipe = usb_rcvctrlpipe(udev, 0);
+ return usb_control_msg(udev, pipe, ATH3K_GETSTATE,
+ USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
+ state, 0x01, USB_CTRL_SET_TIMEOUT);
+}
+
+static int ath3k_get_version(struct usb_device *udev,
+ struct ath3k_version *version)
+{
+ int pipe = 0;
+
+ pipe = usb_rcvctrlpipe(udev, 0);
+ return usb_control_msg(udev, pipe, ATH3K_GETVERSION,
+ USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, version,
+ sizeof(struct ath3k_version),
+ USB_CTRL_SET_TIMEOUT);
+}
+
+static int ath3k_load_fwfile(struct usb_device *udev,
+ const struct firmware *firmware)
+{
+ u8 *send_buf;
+ int err, pipe, len, size, count, sent = 0;
+ int ret;
+
+ count = firmware->size;
+
+ send_buf = kmalloc(BULK_SIZE, GFP_KERNEL);
+ if (!send_buf) {
+ BT_ERR("Can't allocate memory chunk for firmware");
+ return -ENOMEM;
+ }
+
+ size = min_t(uint, count, FW_HDR_SIZE);
+ memcpy(send_buf, firmware->data, size);
+
+ pipe = usb_sndctrlpipe(udev, 0);
+ ret = usb_control_msg(udev, pipe, ATH3K_DNLOAD,
+ USB_TYPE_VENDOR, 0, 0, send_buf,
+ size, USB_CTRL_SET_TIMEOUT);
+ if (ret < 0) {
+ BT_ERR("Can't change to loading configuration err");
+ kfree(send_buf);
+ return ret;
+ }
+
+ sent += size;
+ count -= size;
+
+ while (count) {
+ size = min_t(uint, count, BULK_SIZE);
+ pipe = usb_sndbulkpipe(udev, 0x02);
+
+ memcpy(send_buf, firmware->data + sent, size);
+
+ err = usb_bulk_msg(udev, pipe, send_buf, size,
+ &len, 3000);
+ if (err || (len != size)) {
+ BT_ERR("Error in firmware loading err = %d,"
+ "len = %d, size = %d", err, len, size);
+ kfree(send_buf);
+ return err;
+ }
+ sent += size;
+ count -= size;
+ }
+
+ kfree(send_buf);
+ return 0;
+}
+
+static int ath3k_switch_pid(struct usb_device *udev)
+{
+ int pipe = 0;
+
+ pipe = usb_sndctrlpipe(udev, 0);
+ return usb_control_msg(udev, pipe, USB_REG_SWITCH_VID_PID,
+ USB_TYPE_VENDOR, 0, 0,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+static int ath3k_set_normal_mode(struct usb_device *udev)
+{
+ unsigned char fw_state;
+ int pipe = 0, ret;
+
+ ret = ath3k_get_state(udev, &fw_state);
+ if (ret < 0) {
+ BT_ERR("Can't get state to change to normal mode err");
+ return ret;
+ }
+
+ if ((fw_state & ATH3K_MODE_MASK) == ATH3K_NORMAL_MODE) {
+ BT_DBG("firmware was already in normal mode");
+ return 0;
+ }
+
+ pipe = usb_sndctrlpipe(udev, 0);
+ return usb_control_msg(udev, pipe, ATH3K_SET_NORMAL_MODE,
+ USB_TYPE_VENDOR, 0, 0,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+static int ath3k_load_patch(struct usb_device *udev)
+{
+ unsigned char fw_state;
+ char filename[ATH3K_NAME_LEN] = {0};
+ const struct firmware *firmware;
+ struct ath3k_version fw_version, pt_version;
+ int ret;
+
+ ret = ath3k_get_state(udev, &fw_state);
+ if (ret < 0) {
+ BT_ERR("Can't get state to change to load ram patch err");
+ return ret;
+ }
+
+ if (fw_state & ATH3K_PATCH_UPDATE) {
+ BT_DBG("Patch was already downloaded");
+ return 0;
+ }
+
+ ret = ath3k_get_version(udev, &fw_version);
+ if (ret < 0) {
+ BT_ERR("Can't get version to change to load ram patch err");
+ return ret;
+ }
+
+ snprintf(filename, ATH3K_NAME_LEN, "ar3k/AthrBT_0x%08x.dfu",
+ fw_version.rom_version);
+
+ ret = request_firmware(&firmware, filename, &udev->dev);
+ if (ret < 0) {
+ BT_ERR("Patch file not found %s", filename);
+ return ret;
+ }
+
+ pt_version.rom_version = *(int *)(firmware->data + firmware->size - 8);
+ pt_version.build_version = *(int *)
+ (firmware->data + firmware->size - 4);
+
+ if ((pt_version.rom_version != fw_version.rom_version) ||
+ (pt_version.build_version <= fw_version.build_version)) {
+ BT_ERR("Patch file version did not match with firmware");
+ release_firmware(firmware);
+ return -EINVAL;
+ }
+
+ ret = ath3k_load_fwfile(udev, firmware);
+ release_firmware(firmware);
+
+ return ret;
+}
+
+static int ath3k_load_syscfg(struct usb_device *udev)
+{
+ unsigned char fw_state;
+ char filename[ATH3K_NAME_LEN] = {0};
+ const struct firmware *firmware;
+ struct ath3k_version fw_version;
+ int clk_value, ret;
+
+ ret = ath3k_get_state(udev, &fw_state);
+ if (ret < 0) {
+ BT_ERR("Can't get state to change to load configration err");
+ return -EBUSY;
+ }
+
+ ret = ath3k_get_version(udev, &fw_version);
+ if (ret < 0) {
+ BT_ERR("Can't get version to change to load ram patch err");
+ return ret;
+ }
+
+ switch (fw_version.ref_clock) {
+
+ case ATH3K_XTAL_FREQ_26M:
+ clk_value = 26;
+ break;
+ case ATH3K_XTAL_FREQ_40M:
+ clk_value = 40;
+ break;
+ case ATH3K_XTAL_FREQ_19P2:
+ clk_value = 19;
+ break;
+ default:
+ clk_value = 0;
+ break;
+ }
+
+ snprintf(filename, ATH3K_NAME_LEN, "ar3k/ramps_0x%08x_%d%s",
+ fw_version.rom_version, clk_value, ".dfu");
+
+ ret = request_firmware(&firmware, filename, &udev->dev);
+ if (ret < 0) {
+ BT_ERR("Configuration file not found %s", filename);
+ return ret;
+ }
+
+ ret = ath3k_load_fwfile(udev, firmware);
+ release_firmware(firmware);
+
+ return ret;
+}
+
+static int ath3k_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ const struct firmware *firmware;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ int ret;
+
+ BT_DBG("intf %p id %p", intf, id);
+
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
+
+ /* match device ID in ath3k blacklist table */
+ if (!id->driver_info) {
+ const struct usb_device_id *match;
+ match = usb_match_id(intf, ath3k_blist_tbl);
+ if (match)
+ id = match;
+ }
+
+ /* load patch and sysconfig files for AR3012 */
+ if (id->driver_info & BTUSB_ATH3012) {
+
+ /* New firmware with patch and sysconfig files already loaded */
+ if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x0001)
+ return -ENODEV;
+
+ ret = ath3k_load_patch(udev);
+ if (ret < 0) {
+ BT_ERR("Loading patch file failed");
+ return ret;
+ }
+ ret = ath3k_load_syscfg(udev);
+ if (ret < 0) {
+ BT_ERR("Loading sysconfig file failed");
+ return ret;
+ }
+ ret = ath3k_set_normal_mode(udev);
+ if (ret < 0) {
+ BT_ERR("Set normal mode failed");
+ return ret;
+ }
+ ath3k_switch_pid(udev);
+ return 0;
+ }
+
+ ret = request_firmware(&firmware, ATH3K_FIRMWARE, &udev->dev);
+ if (ret < 0) {
+ if (ret == -ENOENT)
+ BT_ERR("Firmware file \"%s\" not found",
+ ATH3K_FIRMWARE);
+ else
+ BT_ERR("Firmware file \"%s\" request failed (err=%d)",
+ ATH3K_FIRMWARE, ret);
+ return ret;
+ }
+
+ ret = ath3k_load_firmware(udev, firmware);
+ release_firmware(firmware);
+
+ return ret;
+}
+
+static void ath3k_disconnect(struct usb_interface *intf)
+{
+ BT_DBG("ath3k_disconnect intf %p", intf);
+}
+
+static struct usb_driver ath3k_driver = {
+ .name = "ath3k",
+ .probe = ath3k_probe,
+ .disconnect = ath3k_disconnect,
+ .id_table = ath3k_table,
+};
+
+static int __init ath3k_init(void)
+{
+ BT_INFO("Atheros AR30xx firmware driver ver %s", VERSION);
+ return usb_register(&ath3k_driver);
+}
+
+static void __exit ath3k_exit(void)
+{
+ usb_deregister(&ath3k_driver);
+}
+
+module_init(ath3k_init);
+module_exit(ath3k_exit);
+
+MODULE_AUTHOR("Atheros Communications");
+MODULE_DESCRIPTION("Atheros AR30xx firmware driver");
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(ATH3K_FIRMWARE);
diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c
new file mode 100644
index 0000000..54952ab
--- /dev/null
+++ b/drivers/bluetooth/bcm203x.c
@@ -0,0 +1,310 @@
+/*
+ *
+ * Broadcom Blutonium firmware driver
+ *
+ * Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/atomic.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+
+#define VERSION "1.2"
+
+static const struct usb_device_id bcm203x_table[] = {
+ /* Broadcom Blutonium (BCM2033) */
+ { USB_DEVICE(0x0a5c, 0x2033) },
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bcm203x_table);
+
+#define BCM203X_ERROR 0
+#define BCM203X_RESET 1
+#define BCM203X_LOAD_MINIDRV 2
+#define BCM203X_SELECT_MEMORY 3
+#define BCM203X_CHECK_MEMORY 4
+#define BCM203X_LOAD_FIRMWARE 5
+#define BCM203X_CHECK_FIRMWARE 6
+
+#define BCM203X_IN_EP 0x81
+#define BCM203X_OUT_EP 0x02
+
+struct bcm203x_data {
+ struct usb_device *udev;
+
+ unsigned long state;
+
+ struct work_struct work;
+ atomic_t shutdown;
+
+ struct urb *urb;
+ unsigned char *buffer;
+
+ unsigned char *fw_data;
+ unsigned int fw_size;
+ unsigned int fw_sent;
+};
+
+static void bcm203x_complete(struct urb *urb)
+{
+ struct bcm203x_data *data = urb->context;
+ struct usb_device *udev = urb->dev;
+ int len;
+
+ BT_DBG("udev %p urb %p", udev, urb);
+
+ if (urb->status) {
+ BT_ERR("URB failed with status %d", urb->status);
+ data->state = BCM203X_ERROR;
+ return;
+ }
+
+ switch (data->state) {
+ case BCM203X_LOAD_MINIDRV:
+ memcpy(data->buffer, "#", 1);
+
+ usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+ data->buffer, 1, bcm203x_complete, data);
+
+ data->state = BCM203X_SELECT_MEMORY;
+
+ /* use workqueue to have a small delay */
+ schedule_work(&data->work);
+ break;
+
+ case BCM203X_SELECT_MEMORY:
+ usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
+ data->buffer, 32, bcm203x_complete, data, 1);
+
+ data->state = BCM203X_CHECK_MEMORY;
+
+ if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
+ BT_ERR("Can't submit URB");
+ break;
+
+ case BCM203X_CHECK_MEMORY:
+ if (data->buffer[0] != '#') {
+ BT_ERR("Memory select failed");
+ data->state = BCM203X_ERROR;
+ break;
+ }
+
+ data->state = BCM203X_LOAD_FIRMWARE;
+
+ case BCM203X_LOAD_FIRMWARE:
+ if (data->fw_sent == data->fw_size) {
+ usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
+ data->buffer, 32, bcm203x_complete, data, 1);
+
+ data->state = BCM203X_CHECK_FIRMWARE;
+ } else {
+ len = min_t(uint, data->fw_size - data->fw_sent, 4096);
+
+ usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+ data->fw_data + data->fw_sent, len, bcm203x_complete, data);
+
+ data->fw_sent += len;
+ }
+
+ if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
+ BT_ERR("Can't submit URB");
+ break;
+
+ case BCM203X_CHECK_FIRMWARE:
+ if (data->buffer[0] != '.') {
+ BT_ERR("Firmware loading failed");
+ data->state = BCM203X_ERROR;
+ break;
+ }
+
+ data->state = BCM203X_RESET;
+ break;
+ }
+}
+
+static void bcm203x_work(struct work_struct *work)
+{
+ struct bcm203x_data *data =
+ container_of(work, struct bcm203x_data, work);
+
+ if (atomic_read(&data->shutdown))
+ return;
+
+ if (usb_submit_urb(data->urb, GFP_KERNEL) < 0)
+ BT_ERR("Can't submit URB");
+}
+
+static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ const struct firmware *firmware;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct bcm203x_data *data;
+ int size;
+
+ BT_DBG("intf %p id %p", intf, id);
+
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ BT_ERR("Can't allocate memory for data structure");
+ return -ENOMEM;
+ }
+
+ data->udev = udev;
+ data->state = BCM203X_LOAD_MINIDRV;
+
+ data->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!data->urb) {
+ BT_ERR("Can't allocate URB");
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ if (request_firmware(&firmware, "BCM2033-MD.hex", &udev->dev) < 0) {
+ BT_ERR("Mini driver request failed");
+ usb_free_urb(data->urb);
+ kfree(data);
+ return -EIO;
+ }
+
+ BT_DBG("minidrv data %p size %zu", firmware->data, firmware->size);
+
+ size = max_t(uint, firmware->size, 4096);
+
+ data->buffer = kmalloc(size, GFP_KERNEL);
+ if (!data->buffer) {
+ BT_ERR("Can't allocate memory for mini driver");
+ release_firmware(firmware);
+ usb_free_urb(data->urb);
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ memcpy(data->buffer, firmware->data, firmware->size);
+
+ usb_fill_bulk_urb(data->urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+ data->buffer, firmware->size, bcm203x_complete, data);
+
+ release_firmware(firmware);
+
+ if (request_firmware(&firmware, "BCM2033-FW.bin", &udev->dev) < 0) {
+ BT_ERR("Firmware request failed");
+ usb_free_urb(data->urb);
+ kfree(data->buffer);
+ kfree(data);
+ return -EIO;
+ }
+
+ BT_DBG("firmware data %p size %zu", firmware->data, firmware->size);
+
+ data->fw_data = kmemdup(firmware->data, firmware->size, GFP_KERNEL);
+ if (!data->fw_data) {
+ BT_ERR("Can't allocate memory for firmware image");
+ release_firmware(firmware);
+ usb_free_urb(data->urb);
+ kfree(data->buffer);
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ data->fw_size = firmware->size;
+ data->fw_sent = 0;
+
+ release_firmware(firmware);
+
+ INIT_WORK(&data->work, bcm203x_work);
+
+ usb_set_intfdata(intf, data);
+
+ /* use workqueue to have a small delay */
+ schedule_work(&data->work);
+
+ return 0;
+}
+
+static void bcm203x_disconnect(struct usb_interface *intf)
+{
+ struct bcm203x_data *data = usb_get_intfdata(intf);
+
+ BT_DBG("intf %p", intf);
+
+ atomic_inc(&data->shutdown);
+ cancel_work_sync(&data->work);
+
+ usb_kill_urb(data->urb);
+
+ usb_set_intfdata(intf, NULL);
+
+ usb_free_urb(data->urb);
+ kfree(data->fw_data);
+ kfree(data->buffer);
+ kfree(data);
+}
+
+static struct usb_driver bcm203x_driver = {
+ .name = "bcm203x",
+ .probe = bcm203x_probe,
+ .disconnect = bcm203x_disconnect,
+ .id_table = bcm203x_table,
+};
+
+static int __init bcm203x_init(void)
+{
+ int err;
+
+ BT_INFO("Broadcom Blutonium firmware driver ver %s", VERSION);
+
+ err = usb_register(&bcm203x_driver);
+ if (err < 0)
+ BT_ERR("Failed to register USB driver");
+
+ return err;
+}
+
+static void __exit bcm203x_exit(void)
+{
+ usb_deregister(&bcm203x_driver);
+}
+
+module_init(bcm203x_init);
+module_exit(bcm203x_exit);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Broadcom Blutonium firmware driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("BCM2033-MD.hex");
+MODULE_FIRMWARE("BCM2033-FW.bin");
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
new file mode 100644
index 0000000..a936763
--- /dev/null
+++ b/drivers/bluetooth/bfusb.c
@@ -0,0 +1,790 @@
+/*
+ *
+ * AVM BlueFRITZ! USB driver
+ *
+ * Copyright (C) 2003-2006 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#define VERSION "1.2"
+
+static struct usb_driver bfusb_driver;
+
+static struct usb_device_id bfusb_table[] = {
+ /* AVM BlueFRITZ! USB */
+ { USB_DEVICE(0x057c, 0x2200) },
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bfusb_table);
+
+#define BFUSB_MAX_BLOCK_SIZE 256
+
+#define BFUSB_BLOCK_TIMEOUT 3000
+
+#define BFUSB_TX_PROCESS 1
+#define BFUSB_TX_WAKEUP 2
+
+#define BFUSB_MAX_BULK_TX 2
+#define BFUSB_MAX_BULK_RX 2
+
+struct bfusb_data {
+ struct hci_dev *hdev;
+
+ unsigned long state;
+
+ struct usb_device *udev;
+
+ unsigned int bulk_in_ep;
+ unsigned int bulk_out_ep;
+ unsigned int bulk_pkt_size;
+
+ rwlock_t lock;
+
+ struct sk_buff_head transmit_q;
+
+ struct sk_buff *reassembly;
+
+ atomic_t pending_tx;
+ struct sk_buff_head pending_q;
+ struct sk_buff_head completed_q;
+};
+
+struct bfusb_data_scb {
+ struct urb *urb;
+};
+
+static void bfusb_tx_complete(struct urb *urb);
+static void bfusb_rx_complete(struct urb *urb);
+
+static struct urb *bfusb_get_completed(struct bfusb_data *data)
+{
+ struct sk_buff *skb;
+ struct urb *urb = NULL;
+
+ BT_DBG("bfusb %p", data);
+
+ skb = skb_dequeue(&data->completed_q);
+ if (skb) {
+ urb = ((struct bfusb_data_scb *) skb->cb)->urb;
+ kfree_skb(skb);
+ }
+
+ return urb;
+}
+
+static void bfusb_unlink_urbs(struct bfusb_data *data)
+{
+ struct sk_buff *skb;
+ struct urb *urb;
+
+ BT_DBG("bfusb %p", data);
+
+ while ((skb = skb_dequeue(&data->pending_q))) {
+ urb = ((struct bfusb_data_scb *) skb->cb)->urb;
+ usb_kill_urb(urb);
+ skb_queue_tail(&data->completed_q, skb);
+ }
+
+ while ((urb = bfusb_get_completed(data)))
+ usb_free_urb(urb);
+}
+
+static int bfusb_send_bulk(struct bfusb_data *data, struct sk_buff *skb)
+{
+ struct bfusb_data_scb *scb = (void *) skb->cb;
+ struct urb *urb = bfusb_get_completed(data);
+ int err, pipe;
+
+ BT_DBG("bfusb %p skb %p len %d", data, skb, skb->len);
+
+ if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
+ return -ENOMEM;
+
+ pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
+
+ usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, skb->len,
+ bfusb_tx_complete, skb);
+
+ scb->urb = urb;
+
+ skb_queue_tail(&data->pending_q, skb);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ BT_ERR("%s bulk tx submit failed urb %p err %d",
+ data->hdev->name, urb, err);
+ skb_unlink(skb, &data->pending_q);
+ usb_free_urb(urb);
+ } else
+ atomic_inc(&data->pending_tx);
+
+ return err;
+}
+
+static void bfusb_tx_wakeup(struct bfusb_data *data)
+{
+ struct sk_buff *skb;
+
+ BT_DBG("bfusb %p", data);
+
+ if (test_and_set_bit(BFUSB_TX_PROCESS, &data->state)) {
+ set_bit(BFUSB_TX_WAKEUP, &data->state);
+ return;
+ }
+
+ do {
+ clear_bit(BFUSB_TX_WAKEUP, &data->state);
+
+ while ((atomic_read(&data->pending_tx) < BFUSB_MAX_BULK_TX) &&
+ (skb = skb_dequeue(&data->transmit_q))) {
+ if (bfusb_send_bulk(data, skb) < 0) {
+ skb_queue_head(&data->transmit_q, skb);
+ break;
+ }
+ }
+
+ } while (test_bit(BFUSB_TX_WAKEUP, &data->state));
+
+ clear_bit(BFUSB_TX_PROCESS, &data->state);
+}
+
+static void bfusb_tx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = (struct sk_buff *) urb->context;
+ struct bfusb_data *data = (struct bfusb_data *) skb->dev;
+
+ BT_DBG("bfusb %p urb %p skb %p len %d", data, urb, skb, skb->len);
+
+ atomic_dec(&data->pending_tx);
+
+ if (!test_bit(HCI_RUNNING, &data->hdev->flags))
+ return;
+
+ if (!urb->status)
+ data->hdev->stat.byte_tx += skb->len;
+ else
+ data->hdev->stat.err_tx++;
+
+ read_lock(&data->lock);
+
+ skb_unlink(skb, &data->pending_q);
+ skb_queue_tail(&data->completed_q, skb);
+
+ bfusb_tx_wakeup(data);
+
+ read_unlock(&data->lock);
+}
+
+
+static int bfusb_rx_submit(struct bfusb_data *data, struct urb *urb)
+{
+ struct bfusb_data_scb *scb;
+ struct sk_buff *skb;
+ int err, pipe, size = HCI_MAX_FRAME_SIZE + 32;
+
+ BT_DBG("bfusb %p urb %p", data, urb);
+
+ if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
+ return -ENOMEM;
+
+ skb = bt_skb_alloc(size, GFP_ATOMIC);
+ if (!skb) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ skb->dev = (void *) data;
+
+ scb = (struct bfusb_data_scb *) skb->cb;
+ scb->urb = urb;
+
+ pipe = usb_rcvbulkpipe(data->udev, data->bulk_in_ep);
+
+ usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, size,
+ bfusb_rx_complete, skb);
+
+ skb_queue_tail(&data->pending_q, skb);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ BT_ERR("%s bulk rx submit failed urb %p err %d",
+ data->hdev->name, urb, err);
+ skb_unlink(skb, &data->pending_q);
+ kfree_skb(skb);
+ usb_free_urb(urb);
+ }
+
+ return err;
+}
+
+static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned char *buf, int len)
+{
+ BT_DBG("bfusb %p hdr 0x%02x data %p len %d", data, hdr, buf, len);
+
+ if (hdr & 0x10) {
+ BT_ERR("%s error in block", data->hdev->name);
+ kfree_skb(data->reassembly);
+ data->reassembly = NULL;
+ return -EIO;
+ }
+
+ if (hdr & 0x04) {
+ struct sk_buff *skb;
+ unsigned char pkt_type;
+ int pkt_len = 0;
+
+ if (data->reassembly) {
+ BT_ERR("%s unexpected start block", data->hdev->name);
+ kfree_skb(data->reassembly);
+ data->reassembly = NULL;
+ }
+
+ if (len < 1) {
+ BT_ERR("%s no packet type found", data->hdev->name);
+ return -EPROTO;
+ }
+
+ pkt_type = *buf++; len--;
+
+ switch (pkt_type) {
+ case HCI_EVENT_PKT:
+ if (len >= HCI_EVENT_HDR_SIZE) {
+ struct hci_event_hdr *hdr = (struct hci_event_hdr *) buf;
+ pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
+ } else {
+ BT_ERR("%s event block is too short", data->hdev->name);
+ return -EILSEQ;
+ }
+ break;
+
+ case HCI_ACLDATA_PKT:
+ if (len >= HCI_ACL_HDR_SIZE) {
+ struct hci_acl_hdr *hdr = (struct hci_acl_hdr *) buf;
+ pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen);
+ } else {
+ BT_ERR("%s data block is too short", data->hdev->name);
+ return -EILSEQ;
+ }
+ break;
+
+ case HCI_SCODATA_PKT:
+ if (len >= HCI_SCO_HDR_SIZE) {
+ struct hci_sco_hdr *hdr = (struct hci_sco_hdr *) buf;
+ pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
+ } else {
+ BT_ERR("%s audio block is too short", data->hdev->name);
+ return -EILSEQ;
+ }
+ break;
+ }
+
+ skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
+ if (!skb) {
+ BT_ERR("%s no memory for the packet", data->hdev->name);
+ return -ENOMEM;
+ }
+
+ skb->dev = (void *) data->hdev;
+ bt_cb(skb)->pkt_type = pkt_type;
+
+ data->reassembly = skb;
+ } else {
+ if (!data->reassembly) {
+ BT_ERR("%s unexpected continuation block", data->hdev->name);
+ return -EIO;
+ }
+ }
+
+ if (len > 0)
+ memcpy(skb_put(data->reassembly, len), buf, len);
+
+ if (hdr & 0x08) {
+ hci_recv_frame(data->reassembly);
+ data->reassembly = NULL;
+ }
+
+ return 0;
+}
+
+static void bfusb_rx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = (struct sk_buff *) urb->context;
+ struct bfusb_data *data = (struct bfusb_data *) skb->dev;
+ unsigned char *buf = urb->transfer_buffer;
+ int count = urb->actual_length;
+ int err, hdr, len;
+
+ BT_DBG("bfusb %p urb %p skb %p len %d", data, urb, skb, skb->len);
+
+ read_lock(&data->lock);
+
+ if (!test_bit(HCI_RUNNING, &data->hdev->flags))
+ goto unlock;
+
+ if (urb->status || !count)
+ goto resubmit;
+
+ data->hdev->stat.byte_rx += count;
+
+ skb_put(skb, count);
+
+ while (count) {
+ hdr = buf[0] | (buf[1] << 8);
+
+ if (hdr & 0x4000) {
+ len = 0;
+ count -= 2;
+ buf += 2;
+ } else {
+ len = (buf[2] == 0) ? 256 : buf[2];
+ count -= 3;
+ buf += 3;
+ }
+
+ if (count < len) {
+ BT_ERR("%s block extends over URB buffer ranges",
+ data->hdev->name);
+ }
+
+ if ((hdr & 0xe1) == 0xc1)
+ bfusb_recv_block(data, hdr, buf, len);
+
+ count -= len;
+ buf += len;
+ }
+
+ skb_unlink(skb, &data->pending_q);
+ kfree_skb(skb);
+
+ bfusb_rx_submit(data, urb);
+
+ read_unlock(&data->lock);
+
+ return;
+
+resubmit:
+ urb->dev = data->udev;
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ BT_ERR("%s bulk resubmit failed urb %p err %d",
+ data->hdev->name, urb, err);
+ }
+
+unlock:
+ read_unlock(&data->lock);
+}
+
+static int bfusb_open(struct hci_dev *hdev)
+{
+ struct bfusb_data *data = hdev->driver_data;
+ unsigned long flags;
+ int i, err;
+
+ BT_DBG("hdev %p bfusb %p", hdev, data);
+
+ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ write_lock_irqsave(&data->lock, flags);
+
+ err = bfusb_rx_submit(data, NULL);
+ if (!err) {
+ for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
+ bfusb_rx_submit(data, NULL);
+ } else {
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ }
+
+ write_unlock_irqrestore(&data->lock, flags);
+
+ return err;
+}
+
+static int bfusb_flush(struct hci_dev *hdev)
+{
+ struct bfusb_data *data = hdev->driver_data;
+
+ BT_DBG("hdev %p bfusb %p", hdev, data);
+
+ skb_queue_purge(&data->transmit_q);
+
+ return 0;
+}
+
+static int bfusb_close(struct hci_dev *hdev)
+{
+ struct bfusb_data *data = hdev->driver_data;
+ unsigned long flags;
+
+ BT_DBG("hdev %p bfusb %p", hdev, data);
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ write_lock_irqsave(&data->lock, flags);
+ write_unlock_irqrestore(&data->lock, flags);
+
+ bfusb_unlink_urbs(data);
+ bfusb_flush(hdev);
+
+ return 0;
+}
+
+static int bfusb_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct bfusb_data *data;
+ struct sk_buff *nskb;
+ unsigned char buf[3];
+ int sent = 0, size, count;
+
+ BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len);
+
+ if (!hdev) {
+ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ data = hdev->driver_data;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+ };
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+ count = skb->len;
+
+ /* Max HCI frame size seems to be 1511 + 1 */
+ nskb = bt_skb_alloc(count + 32, GFP_ATOMIC);
+ if (!nskb) {
+ BT_ERR("Can't allocate memory for new packet");
+ return -ENOMEM;
+ }
+
+ nskb->dev = (void *) data;
+
+ while (count) {
+ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE);
+
+ buf[0] = 0xc1 | ((sent == 0) ? 0x04 : 0) | ((count == size) ? 0x08 : 0);
+ buf[1] = 0x00;
+ buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size;
+
+ memcpy(skb_put(nskb, 3), buf, 3);
+ skb_copy_from_linear_data_offset(skb, sent, skb_put(nskb, size), size);
+
+ sent += size;
+ count -= size;
+ }
+
+ /* Don't send frame with multiple size of bulk max packet */
+ if ((nskb->len % data->bulk_pkt_size) == 0) {
+ buf[0] = 0xdd;
+ buf[1] = 0x00;
+ memcpy(skb_put(nskb, 2), buf, 2);
+ }
+
+ read_lock(&data->lock);
+
+ skb_queue_tail(&data->transmit_q, nskb);
+ bfusb_tx_wakeup(data);
+
+ read_unlock(&data->lock);
+
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static void bfusb_destruct(struct hci_dev *hdev)
+{
+ struct bfusb_data *data = hdev->driver_data;
+
+ BT_DBG("hdev %p bfusb %p", hdev, data);
+
+ kfree(data);
+}
+
+static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+static int bfusb_load_firmware(struct bfusb_data *data,
+ const unsigned char *firmware, int count)
+{
+ unsigned char *buf;
+ int err, pipe, len, size, sent = 0;
+
+ BT_DBG("bfusb %p udev %p", data, data->udev);
+
+ BT_INFO("BlueFRITZ! USB loading firmware");
+
+ buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_KERNEL);
+ if (!buf) {
+ BT_ERR("Can't allocate memory chunk for firmware");
+ return -ENOMEM;
+ }
+
+ pipe = usb_sndctrlpipe(data->udev, 0);
+
+ if (usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ 0, 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT) < 0) {
+ BT_ERR("Can't change to loading configuration");
+ kfree(buf);
+ return -EBUSY;
+ }
+
+ data->udev->toggle[0] = data->udev->toggle[1] = 0;
+
+ pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
+
+ while (count) {
+ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3);
+
+ memcpy(buf, firmware + sent, size);
+
+ err = usb_bulk_msg(data->udev, pipe, buf, size,
+ &len, BFUSB_BLOCK_TIMEOUT);
+
+ if (err || (len != size)) {
+ BT_ERR("Error in firmware loading");
+ goto error;
+ }
+
+ sent += size;
+ count -= size;
+ }
+
+ err = usb_bulk_msg(data->udev, pipe, NULL, 0,
+ &len, BFUSB_BLOCK_TIMEOUT);
+ if (err < 0) {
+ BT_ERR("Error in null packet request");
+ goto error;
+ }
+
+ pipe = usb_sndctrlpipe(data->udev, 0);
+
+ err = usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ 0, 2, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (err < 0) {
+ BT_ERR("Can't change to running configuration");
+ goto error;
+ }
+
+ data->udev->toggle[0] = data->udev->toggle[1] = 0;
+
+ BT_INFO("BlueFRITZ! USB device ready");
+
+ kfree(buf);
+ return 0;
+
+error:
+ kfree(buf);
+
+ pipe = usb_sndctrlpipe(data->udev, 0);
+
+ usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ 0, 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+
+ return err;
+}
+
+static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ const struct firmware *firmware;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_host_endpoint *bulk_out_ep;
+ struct usb_host_endpoint *bulk_in_ep;
+ struct hci_dev *hdev;
+ struct bfusb_data *data;
+
+ BT_DBG("intf %p id %p", intf, id);
+
+ /* Check number of endpoints */
+ if (intf->cur_altsetting->desc.bNumEndpoints < 2)
+ return -EIO;
+
+ bulk_out_ep = &intf->cur_altsetting->endpoint[0];
+ bulk_in_ep = &intf->cur_altsetting->endpoint[1];
+
+ if (!bulk_out_ep || !bulk_in_ep) {
+ BT_ERR("Bulk endpoints not found");
+ goto done;
+ }
+
+ /* Initialize control structure and load firmware */
+ data = kzalloc(sizeof(struct bfusb_data), GFP_KERNEL);
+ if (!data) {
+ BT_ERR("Can't allocate memory for control structure");
+ goto done;
+ }
+
+ data->udev = udev;
+ data->bulk_in_ep = bulk_in_ep->desc.bEndpointAddress;
+ data->bulk_out_ep = bulk_out_ep->desc.bEndpointAddress;
+ data->bulk_pkt_size = le16_to_cpu(bulk_out_ep->desc.wMaxPacketSize);
+
+ rwlock_init(&data->lock);
+
+ data->reassembly = NULL;
+
+ skb_queue_head_init(&data->transmit_q);
+ skb_queue_head_init(&data->pending_q);
+ skb_queue_head_init(&data->completed_q);
+
+ if (request_firmware(&firmware, "bfubase.frm", &udev->dev) < 0) {
+ BT_ERR("Firmware request failed");
+ goto error;
+ }
+
+ BT_DBG("firmware data %p size %zu", firmware->data, firmware->size);
+
+ if (bfusb_load_firmware(data, firmware->data, firmware->size) < 0) {
+ BT_ERR("Firmware loading failed");
+ goto release;
+ }
+
+ release_firmware(firmware);
+
+ /* Initialize and register HCI device */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BT_ERR("Can't allocate HCI device");
+ goto error;
+ }
+
+ data->hdev = hdev;
+
+ hdev->bus = HCI_USB;
+ hdev->driver_data = data;
+ SET_HCIDEV_DEV(hdev, &intf->dev);
+
+ hdev->open = bfusb_open;
+ hdev->close = bfusb_close;
+ hdev->flush = bfusb_flush;
+ hdev->send = bfusb_send_frame;
+ hdev->destruct = bfusb_destruct;
+ hdev->ioctl = bfusb_ioctl;
+
+ hdev->owner = THIS_MODULE;
+
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ hci_free_dev(hdev);
+ goto error;
+ }
+
+ usb_set_intfdata(intf, data);
+
+ return 0;
+
+release:
+ release_firmware(firmware);
+
+error:
+ kfree(data);
+
+done:
+ return -EIO;
+}
+
+static void bfusb_disconnect(struct usb_interface *intf)
+{
+ struct bfusb_data *data = usb_get_intfdata(intf);
+ struct hci_dev *hdev = data->hdev;
+
+ BT_DBG("intf %p", intf);
+
+ if (!hdev)
+ return;
+
+ usb_set_intfdata(intf, NULL);
+
+ bfusb_close(hdev);
+
+ hci_unregister_dev(hdev);
+ hci_free_dev(hdev);
+}
+
+static struct usb_driver bfusb_driver = {
+ .name = "bfusb",
+ .probe = bfusb_probe,
+ .disconnect = bfusb_disconnect,
+ .id_table = bfusb_table,
+};
+
+static int __init bfusb_init(void)
+{
+ int err;
+
+ BT_INFO("BlueFRITZ! USB driver ver %s", VERSION);
+
+ err = usb_register(&bfusb_driver);
+ if (err < 0)
+ BT_ERR("Failed to register BlueFRITZ! USB driver");
+
+ return err;
+}
+
+static void __exit bfusb_exit(void)
+{
+ usb_deregister(&bfusb_driver);
+}
+
+module_init(bfusb_init);
+module_exit(bfusb_exit);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("bfubase.frm");
diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
new file mode 100644
index 0000000..f5a857f
--- /dev/null
+++ b/drivers/bluetooth/bluecard_cs.c
@@ -0,0 +1,1034 @@
+/*
+ *
+ * Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)
+ *
+ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+#include <linux/wait.h>
+
+#include <linux/skbuff.h>
+#include <linux/io.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct bluecard_info_t {
+ struct pcmcia_device *p_dev;
+
+ struct hci_dev *hdev;
+
+ spinlock_t lock; /* For serializing operations */
+ struct timer_list timer; /* For LED control */
+
+ struct sk_buff_head txq;
+ unsigned long tx_state;
+
+ unsigned long rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+
+ unsigned char ctrl_reg;
+ unsigned long hw_state; /* Status of the hardware and LED control */
+} bluecard_info_t;
+
+
+static int bluecard_config(struct pcmcia_device *link);
+static void bluecard_release(struct pcmcia_device *link);
+
+static void bluecard_detach(struct pcmcia_device *p_dev);
+
+
+/* Default baud rate: 57600, 115200, 230400 or 460800 */
+#define DEFAULT_BAUD_RATE 230400
+
+
+/* Hardware states */
+#define CARD_READY 1
+#define CARD_HAS_PCCARD_ID 4
+#define CARD_HAS_POWER_LED 5
+#define CARD_HAS_ACTIVITY_LED 6
+
+/* Transmit states */
+#define XMIT_SENDING 1
+#define XMIT_WAKEUP 2
+#define XMIT_BUFFER_NUMBER 5 /* unset = buffer one, set = buffer two */
+#define XMIT_BUF_ONE_READY 6
+#define XMIT_BUF_TWO_READY 7
+#define XMIT_SENDING_READY 8
+
+/* Receiver states */
+#define RECV_WAIT_PACKET_TYPE 0
+#define RECV_WAIT_EVENT_HEADER 1
+#define RECV_WAIT_ACL_HEADER 2
+#define RECV_WAIT_SCO_HEADER 3
+#define RECV_WAIT_DATA 4
+
+/* Special packet types */
+#define PKT_BAUD_RATE_57600 0x80
+#define PKT_BAUD_RATE_115200 0x81
+#define PKT_BAUD_RATE_230400 0x82
+#define PKT_BAUD_RATE_460800 0x83
+
+
+/* These are the register offsets */
+#define REG_COMMAND 0x20
+#define REG_INTERRUPT 0x21
+#define REG_CONTROL 0x22
+#define REG_RX_CONTROL 0x24
+#define REG_CARD_RESET 0x30
+#define REG_LED_CTRL 0x30
+
+/* REG_COMMAND */
+#define REG_COMMAND_TX_BUF_ONE 0x01
+#define REG_COMMAND_TX_BUF_TWO 0x02
+#define REG_COMMAND_RX_BUF_ONE 0x04
+#define REG_COMMAND_RX_BUF_TWO 0x08
+#define REG_COMMAND_RX_WIN_ONE 0x00
+#define REG_COMMAND_RX_WIN_TWO 0x10
+
+/* REG_CONTROL */
+#define REG_CONTROL_BAUD_RATE_57600 0x00
+#define REG_CONTROL_BAUD_RATE_115200 0x01
+#define REG_CONTROL_BAUD_RATE_230400 0x02
+#define REG_CONTROL_BAUD_RATE_460800 0x03
+#define REG_CONTROL_RTS 0x04
+#define REG_CONTROL_BT_ON 0x08
+#define REG_CONTROL_BT_RESET 0x10
+#define REG_CONTROL_BT_RES_PU 0x20
+#define REG_CONTROL_INTERRUPT 0x40
+#define REG_CONTROL_CARD_RESET 0x80
+
+/* REG_RX_CONTROL */
+#define RTS_LEVEL_SHIFT_BITS 0x02
+
+
+
+/* ======================== LED handling routines ======================== */
+
+
+static void bluecard_activity_led_timeout(u_long arg)
+{
+ bluecard_info_t *info = (bluecard_info_t *)arg;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+ unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+
+
+ if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
+ return;
+
+ if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
+ /* Disable activity LED */
+ outb(0x08 | 0x20, iobase + 0x30);
+ } else {
+ /* Disable power LED */
+ outb(0x00, iobase + 0x30);
+ }
+}
+
+
+static void bluecard_enable_activity_led(bluecard_info_t *info)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+ unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+
+ if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
+ return;
+
+ if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
+ /* Enable activity LED */
+ outb(0x10 | 0x40, iobase + 0x30);
+
+ /* Stop the LED after HZ/4 */
+ mod_timer(&(info->timer), jiffies + HZ / 4);
+ } else {
+ /* Enable power LED */
+ outb(0x08 | 0x20, iobase + 0x30);
+
+ /* Stop the LED after HZ/2 */
+ mod_timer(&(info->timer), jiffies + HZ / 2);
+ }
+}
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int bluecard_write(unsigned int iobase, unsigned int offset, __u8 *buf, int len)
+{
+ int i, actual;
+
+ actual = (len > 15) ? 15 : len;
+
+ outb_p(actual, iobase + offset);
+
+ for (i = 0; i < actual; i++)
+ outb_p(buf[i], iobase + offset + i + 1);
+
+ return actual;
+}
+
+
+static void bluecard_write_wakeup(bluecard_info_t *info)
+{
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+ if (!test_bit(XMIT_SENDING_READY, &(info->tx_state)))
+ return;
+
+ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
+ set_bit(XMIT_WAKEUP, &(info->tx_state));
+ return;
+ }
+
+ do {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ register unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+ register unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+ register unsigned int offset;
+ register unsigned char command;
+ register unsigned long ready_bit;
+ register struct sk_buff *skb;
+ register int len;
+
+ clear_bit(XMIT_WAKEUP, &(info->tx_state));
+
+ if (!pcmcia_dev_present(info->p_dev))
+ return;
+
+ if (test_bit(XMIT_BUFFER_NUMBER, &(info->tx_state))) {
+ if (!test_bit(XMIT_BUF_TWO_READY, &(info->tx_state)))
+ break;
+ offset = 0x10;
+ command = REG_COMMAND_TX_BUF_TWO;
+ ready_bit = XMIT_BUF_TWO_READY;
+ } else {
+ if (!test_bit(XMIT_BUF_ONE_READY, &(info->tx_state)))
+ break;
+ offset = 0x00;
+ command = REG_COMMAND_TX_BUF_ONE;
+ ready_bit = XMIT_BUF_ONE_READY;
+ }
+
+ if (!(skb = skb_dequeue(&(info->txq))))
+ break;
+
+ if (bt_cb(skb)->pkt_type & 0x80) {
+ /* Disable RTS */
+ info->ctrl_reg |= REG_CONTROL_RTS;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+ }
+
+ /* Activate LED */
+ bluecard_enable_activity_led(info);
+
+ /* Send frame */
+ len = bluecard_write(iobase, offset, skb->data, skb->len);
+
+ /* Tell the FPGA to send the data */
+ outb_p(command, iobase + REG_COMMAND);
+
+ /* Mark the buffer as dirty */
+ clear_bit(ready_bit, &(info->tx_state));
+
+ if (bt_cb(skb)->pkt_type & 0x80) {
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
+ DEFINE_WAIT(wait);
+
+ unsigned char baud_reg;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case PKT_BAUD_RATE_460800:
+ baud_reg = REG_CONTROL_BAUD_RATE_460800;
+ break;
+ case PKT_BAUD_RATE_230400:
+ baud_reg = REG_CONTROL_BAUD_RATE_230400;
+ break;
+ case PKT_BAUD_RATE_115200:
+ baud_reg = REG_CONTROL_BAUD_RATE_115200;
+ break;
+ case PKT_BAUD_RATE_57600:
+ /* Fall through... */
+ default:
+ baud_reg = REG_CONTROL_BAUD_RATE_57600;
+ break;
+ }
+
+ /* Wait until the command reaches the baseband */
+ prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/10);
+ finish_wait(&wq, &wait);
+
+ /* Set baud on baseband */
+ info->ctrl_reg &= ~0x03;
+ info->ctrl_reg |= baud_reg;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Enable RTS */
+ info->ctrl_reg &= ~REG_CONTROL_RTS;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Wait before the next HCI packet can be send */
+ prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+ finish_wait(&wq, &wait);
+ }
+
+ if (len == skb->len) {
+ kfree_skb(skb);
+ } else {
+ skb_pull(skb, len);
+ skb_queue_head(&(info->txq), skb);
+ }
+
+ info->hdev->stat.byte_tx += len;
+
+ /* Change buffer */
+ change_bit(XMIT_BUFFER_NUMBER, &(info->tx_state));
+
+ } while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
+
+ clear_bit(XMIT_SENDING, &(info->tx_state));
+}
+
+
+static int bluecard_read(unsigned int iobase, unsigned int offset, __u8 *buf, int size)
+{
+ int i, n, len;
+
+ outb(REG_COMMAND_RX_WIN_ONE, iobase + REG_COMMAND);
+
+ len = inb(iobase + offset);
+ n = 0;
+ i = 1;
+
+ while (n < len) {
+
+ if (i == 16) {
+ outb(REG_COMMAND_RX_WIN_TWO, iobase + REG_COMMAND);
+ i = 0;
+ }
+
+ buf[n] = inb(iobase + offset + i);
+
+ n++;
+ i++;
+
+ }
+
+ return len;
+}
+
+
+static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
+{
+ unsigned int iobase;
+ unsigned char buf[31];
+ int i, len;
+
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ iobase = info->p_dev->resource[0]->start;
+#else
+ iobase = info->p_dev->io.BasePort1;
+#endif
+
+ if (test_bit(XMIT_SENDING_READY, &(info->tx_state)))
+ bluecard_enable_activity_led(info);
+
+ len = bluecard_read(iobase, offset, buf, sizeof(buf));
+
+ for (i = 0; i < len; i++) {
+
+ /* Allocate packet */
+ if (info->rx_skb == NULL) {
+ info->rx_state = RECV_WAIT_PACKET_TYPE;
+ info->rx_count = 0;
+ if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate mem for new packet");
+ return;
+ }
+ }
+
+ if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
+
+ info->rx_skb->dev = (void *) info->hdev;
+ bt_cb(info->rx_skb)->pkt_type = buf[i];
+
+ switch (bt_cb(info->rx_skb)->pkt_type) {
+
+ case 0x00:
+ /* init packet */
+ if (offset != 0x00) {
+ set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+ set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+ set_bit(XMIT_SENDING_READY, &(info->tx_state));
+ bluecard_write_wakeup(info);
+ }
+
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+
+ case HCI_EVENT_PKT:
+ info->rx_state = RECV_WAIT_EVENT_HEADER;
+ info->rx_count = HCI_EVENT_HDR_SIZE;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ info->rx_state = RECV_WAIT_ACL_HEADER;
+ info->rx_count = HCI_ACL_HDR_SIZE;
+ break;
+
+ case HCI_SCODATA_PKT:
+ info->rx_state = RECV_WAIT_SCO_HEADER;
+ info->rx_count = HCI_SCO_HDR_SIZE;
+ break;
+
+ default:
+ /* unknown packet */
+ BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+ info->hdev->stat.err_rx++;
+
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+
+ }
+
+ } else {
+
+ *skb_put(info->rx_skb, 1) = buf[i];
+ info->rx_count--;
+
+ if (info->rx_count == 0) {
+
+ int dlen;
+ struct hci_event_hdr *eh;
+ struct hci_acl_hdr *ah;
+ struct hci_sco_hdr *sh;
+
+ switch (info->rx_state) {
+
+ case RECV_WAIT_EVENT_HEADER:
+ eh = hci_event_hdr(info->rx_skb);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = eh->plen;
+ break;
+
+ case RECV_WAIT_ACL_HEADER:
+ ah = hci_acl_hdr(info->rx_skb);
+ dlen = __le16_to_cpu(ah->dlen);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = dlen;
+ break;
+
+ case RECV_WAIT_SCO_HEADER:
+ sh = hci_sco_hdr(info->rx_skb);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = sh->dlen;
+ break;
+
+ case RECV_WAIT_DATA:
+ hci_recv_frame(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+
+ }
+
+ }
+
+ }
+
+
+ }
+
+ info->hdev->stat.byte_rx += len;
+}
+
+
+static irqreturn_t bluecard_interrupt(int irq, void *dev_inst)
+{
+ bluecard_info_t *info = dev_inst;
+ unsigned int iobase;
+ unsigned char reg;
+
+ if (!info || !info->hdev)
+ /* our irq handler is shared */
+ return IRQ_NONE;
+
+ if (!test_bit(CARD_READY, &(info->hw_state)))
+ return IRQ_HANDLED;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ iobase = info->p_dev->resource[0]->start;
+#else
+ iobase = info->p_dev->io.BasePort1;
+#endif
+
+ spin_lock(&(info->lock));
+
+ /* Disable interrupt */
+ info->ctrl_reg &= ~REG_CONTROL_INTERRUPT;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ reg = inb(iobase + REG_INTERRUPT);
+
+ if ((reg != 0x00) && (reg != 0xff)) {
+
+ if (reg & 0x04) {
+ bluecard_receive(info, 0x00);
+ outb(0x04, iobase + REG_INTERRUPT);
+ outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
+ }
+
+ if (reg & 0x08) {
+ bluecard_receive(info, 0x10);
+ outb(0x08, iobase + REG_INTERRUPT);
+ outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
+ }
+
+ if (reg & 0x01) {
+ set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+ outb(0x01, iobase + REG_INTERRUPT);
+ bluecard_write_wakeup(info);
+ }
+
+ if (reg & 0x02) {
+ set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+ outb(0x02, iobase + REG_INTERRUPT);
+ bluecard_write_wakeup(info);
+ }
+
+ }
+
+ /* Enable interrupt */
+ info->ctrl_reg |= REG_CONTROL_INTERRUPT;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ spin_unlock(&(info->lock));
+
+ return IRQ_HANDLED;
+}
+
+
+
+/* ======================== Device specific HCI commands ======================== */
+
+
+static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
+{
+ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+ struct sk_buff *skb;
+
+ /* Ericsson baud rate command */
+ unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 };
+
+ if (!(skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate mem for new packet");
+ return -1;
+ }
+
+ switch (baud) {
+ case 460800:
+ cmd[4] = 0x00;
+ bt_cb(skb)->pkt_type = PKT_BAUD_RATE_460800;
+ break;
+ case 230400:
+ cmd[4] = 0x01;
+ bt_cb(skb)->pkt_type = PKT_BAUD_RATE_230400;
+ break;
+ case 115200:
+ cmd[4] = 0x02;
+ bt_cb(skb)->pkt_type = PKT_BAUD_RATE_115200;
+ break;
+ case 57600:
+ /* Fall through... */
+ default:
+ cmd[4] = 0x03;
+ bt_cb(skb)->pkt_type = PKT_BAUD_RATE_57600;
+ break;
+ }
+
+ memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
+
+ skb_queue_tail(&(info->txq), skb);
+
+ bluecard_write_wakeup(info);
+
+ return 0;
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int bluecard_hci_flush(struct hci_dev *hdev)
+{
+ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+
+ /* Drop TX queue */
+ skb_queue_purge(&(info->txq));
+
+ return 0;
+}
+
+
+static int bluecard_hci_open(struct hci_dev *hdev)
+{
+ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+ unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+
+ if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
+ bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
+
+ if (test_and_set_bit(HCI_RUNNING, &(hdev->flags)))
+ return 0;
+
+ if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
+ /* Enable LED */
+ outb(0x08 | 0x20, iobase + 0x30);
+ }
+
+ return 0;
+}
+
+
+static int bluecard_hci_close(struct hci_dev *hdev)
+{
+ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+ unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+
+ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+ return 0;
+
+ bluecard_hci_flush(hdev);
+
+ if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
+ /* Disable LED */
+ outb(0x00, iobase + 0x30);
+ }
+
+ return 0;
+}
+
+
+static int bluecard_hci_send_frame(struct sk_buff *skb)
+{
+ bluecard_info_t *info;
+ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+
+ if (!hdev) {
+ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+ info = (bluecard_info_t *)(hdev->driver_data);
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+ };
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ skb_queue_tail(&(info->txq), skb);
+
+ bluecard_write_wakeup(info);
+
+ return 0;
+}
+
+
+static void bluecard_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+static int bluecard_open(bluecard_info_t *info)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+ unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+ struct hci_dev *hdev;
+ unsigned char id;
+
+ spin_lock_init(&(info->lock));
+
+ init_timer(&(info->timer));
+ info->timer.function = &bluecard_activity_led_timeout;
+ info->timer.data = (u_long)info;
+
+ skb_queue_head_init(&(info->txq));
+
+ info->rx_state = RECV_WAIT_PACKET_TYPE;
+ info->rx_count = 0;
+ info->rx_skb = NULL;
+
+ /* Initialize HCI device */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BT_ERR("Can't allocate HCI device");
+ return -ENOMEM;
+ }
+
+ info->hdev = hdev;
+
+ hdev->bus = HCI_PCCARD;
+ hdev->driver_data = info;
+ SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
+
+ hdev->open = bluecard_hci_open;
+ hdev->close = bluecard_hci_close;
+ hdev->flush = bluecard_hci_flush;
+ hdev->send = bluecard_hci_send_frame;
+ hdev->destruct = bluecard_hci_destruct;
+ hdev->ioctl = bluecard_hci_ioctl;
+
+ hdev->owner = THIS_MODULE;
+
+ id = inb(iobase + 0x30);
+
+ if ((id & 0x0f) == 0x02)
+ set_bit(CARD_HAS_PCCARD_ID, &(info->hw_state));
+
+ if (id & 0x10)
+ set_bit(CARD_HAS_POWER_LED, &(info->hw_state));
+
+ if (id & 0x20)
+ set_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state));
+
+ /* Reset card */
+ info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Turn FPGA off */
+ outb(0x80, iobase + 0x30);
+
+ /* Wait some time */
+ msleep(10);
+
+ /* Turn FPGA on */
+ outb(0x00, iobase + 0x30);
+
+ /* Activate card */
+ info->ctrl_reg = REG_CONTROL_BT_ON | REG_CONTROL_BT_RES_PU;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Enable interrupt */
+ outb(0xff, iobase + REG_INTERRUPT);
+ info->ctrl_reg |= REG_CONTROL_INTERRUPT;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ if ((id & 0x0f) == 0x03) {
+ /* Disable RTS */
+ info->ctrl_reg |= REG_CONTROL_RTS;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Set baud rate */
+ info->ctrl_reg |= 0x03;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Enable RTS */
+ info->ctrl_reg &= ~REG_CONTROL_RTS;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+ set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+ set_bit(XMIT_SENDING_READY, &(info->tx_state));
+ }
+
+ /* Start the RX buffers */
+ outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
+ outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
+
+ /* Signal that the hardware is ready */
+ set_bit(CARD_READY, &(info->hw_state));
+
+ /* Drop TX queue */
+ skb_queue_purge(&(info->txq));
+
+ /* Control the point at which RTS is enabled */
+ outb((0x0f << RTS_LEVEL_SHIFT_BITS) | 1, iobase + REG_RX_CONTROL);
+
+ /* Timeout before it is safe to send the first HCI packet */
+ msleep(1250);
+
+ /* Register HCI device */
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ info->hdev = NULL;
+ hci_free_dev(hdev);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+
+static int bluecard_close(bluecard_info_t *info)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+ unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+ struct hci_dev *hdev = info->hdev;
+
+ if (!hdev)
+ return -ENODEV;
+
+ bluecard_hci_close(hdev);
+
+ clear_bit(CARD_READY, &(info->hw_state));
+
+ /* Reset card */
+ info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Turn FPGA off */
+ outb(0x80, iobase + 0x30);
+
+ hci_unregister_dev(hdev);
+ hci_free_dev(hdev);
+
+ return 0;
+}
+
+static int bluecard_probe(struct pcmcia_device *link)
+{
+ bluecard_info_t *info;
+
+ /* Create new info device */
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->p_dev = link;
+ link->priv = info;
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35))
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+
+ link->irq.Handler = bluecard_interrupt;
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37))
+ link->config_flags |= CONF_ENABLE_IRQ;
+#else
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+#endif
+
+ return bluecard_config(link);
+}
+
+
+static void bluecard_detach(struct pcmcia_device *link)
+{
+ bluecard_info_t *info = link->priv;
+
+ bluecard_release(link);
+ kfree(info);
+}
+
+
+static int bluecard_config(struct pcmcia_device *link)
+{
+ bluecard_info_t *info = link->priv;
+ int i, n;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37))
+ link->config_index = 0x20;
+#else
+ link->conf.ConfigIndex = 0x20;
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+ link->resource[0]->end = 64;
+ link->io_lines = 6;
+#else
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.NumPorts1 = 64;
+ link->io.IOAddrLines = 6;
+#endif
+
+ for (n = 0; n < 0x400; n += 0x40) {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ link->resource[0]->start = n ^ 0x300;
+ i = pcmcia_request_io(link);
+#else
+ link->io.BasePort1 = n ^ 0x300;
+ i = pcmcia_request_io(link, &link->io);
+#endif
+ if (i == 0)
+ break;
+ }
+
+ if (i != 0)
+ goto failed;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ i = pcmcia_request_irq(link, bluecard_interrupt);
+ if (i != 0)
+ goto failed;
+#else
+ i = pcmcia_request_irq(link, &link->irq);
+ if (i != 0)
+ link->irq.AssignedIRQ = 0;
+#endif
+
+ i = pcmcia_enable_device(link);
+ if (i != 0)
+ goto failed;
+
+ if (bluecard_open(info) != 0)
+ goto failed;
+
+ return 0;
+
+failed:
+ bluecard_release(link);
+ return -ENODEV;
+}
+
+
+static void bluecard_release(struct pcmcia_device *link)
+{
+ bluecard_info_t *info = link->priv;
+
+ bluecard_close(info);
+
+ del_timer(&(info->timer));
+
+ pcmcia_disable_device(link);
+}
+
+static const struct pcmcia_device_id bluecard_ids[] = {
+ PCMCIA_DEVICE_PROD_ID12("BlueCard", "LSE041", 0xbaf16fbf, 0x657cc15e),
+ PCMCIA_DEVICE_PROD_ID12("BTCFCARD", "LSE139", 0xe3987764, 0x2524b59c),
+ PCMCIA_DEVICE_PROD_ID12("WSS", "LSE039", 0x0a0736ec, 0x24e6dfab),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, bluecard_ids);
+
+static struct pcmcia_driver bluecard_driver = {
+ .owner = THIS_MODULE,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37))
+ .name = "bluecard_cs",
+#else
+ .drv = {
+ .name = "bluecard_cs",
+ },
+#endif
+ .probe = bluecard_probe,
+ .remove = bluecard_detach,
+ .id_table = bluecard_ids,
+};
+
+static int __init init_bluecard_cs(void)
+{
+ return pcmcia_register_driver(&bluecard_driver);
+}
+
+
+static void __exit exit_bluecard_cs(void)
+{
+ pcmcia_unregister_driver(&bluecard_driver);
+}
+
+module_init(init_bluecard_cs);
+module_exit(exit_bluecard_cs);
diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c
new file mode 100644
index 0000000..751b338
--- /dev/null
+++ b/drivers/bluetooth/bpa10x.c
@@ -0,0 +1,542 @@
+/*
+ *
+ * Digianswer Bluetooth USB driver
+ *
+ * Copyright (C) 2004-2007 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#define VERSION "0.10"
+
+static struct usb_device_id bpa10x_table[] = {
+ /* Tektronix BPA 100/105 (Digianswer) */
+ { USB_DEVICE(0x08fd, 0x0002) },
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bpa10x_table);
+
+struct bpa10x_data {
+ struct hci_dev *hdev;
+ struct usb_device *udev;
+
+ struct usb_anchor tx_anchor;
+ struct usb_anchor rx_anchor;
+
+ struct sk_buff *rx_skb[2];
+};
+
+#define HCI_VENDOR_HDR_SIZE 5
+
+struct hci_vendor_hdr {
+ __u8 type;
+ __le16 snum;
+ __le16 dlen;
+} __packed;
+
+static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count)
+{
+ struct bpa10x_data *data = hdev->driver_data;
+
+ BT_DBG("%s queue %d buffer %p count %d", hdev->name,
+ queue, buf, count);
+
+ if (queue < 0 || queue > 1)
+ return -EILSEQ;
+
+ hdev->stat.byte_rx += count;
+
+ while (count) {
+ struct sk_buff *skb = data->rx_skb[queue];
+ struct { __u8 type; int expect; } *scb;
+ int type, len = 0;
+
+ if (!skb) {
+ /* Start of the frame */
+
+ type = *((__u8 *) buf);
+ count--; buf++;
+
+ switch (type) {
+ case HCI_EVENT_PKT:
+ if (count >= HCI_EVENT_HDR_SIZE) {
+ struct hci_event_hdr *h = buf;
+ len = HCI_EVENT_HDR_SIZE + h->plen;
+ } else
+ return -EILSEQ;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ if (count >= HCI_ACL_HDR_SIZE) {
+ struct hci_acl_hdr *h = buf;
+ len = HCI_ACL_HDR_SIZE +
+ __le16_to_cpu(h->dlen);
+ } else
+ return -EILSEQ;
+ break;
+
+ case HCI_SCODATA_PKT:
+ if (count >= HCI_SCO_HDR_SIZE) {
+ struct hci_sco_hdr *h = buf;
+ len = HCI_SCO_HDR_SIZE + h->dlen;
+ } else
+ return -EILSEQ;
+ break;
+
+ case HCI_VENDOR_PKT:
+ if (count >= HCI_VENDOR_HDR_SIZE) {
+ struct hci_vendor_hdr *h = buf;
+ len = HCI_VENDOR_HDR_SIZE +
+ __le16_to_cpu(h->dlen);
+ } else
+ return -EILSEQ;
+ break;
+ }
+
+ skb = bt_skb_alloc(len, GFP_ATOMIC);
+ if (!skb) {
+ BT_ERR("%s no memory for packet", hdev->name);
+ return -ENOMEM;
+ }
+
+ skb->dev = (void *) hdev;
+
+ data->rx_skb[queue] = skb;
+
+ scb = (void *) skb->cb;
+ scb->type = type;
+ scb->expect = len;
+ } else {
+ /* Continuation */
+
+ scb = (void *) skb->cb;
+ len = scb->expect;
+ }
+
+ len = min(len, count);
+
+ memcpy(skb_put(skb, len), buf, len);
+
+ scb->expect -= len;
+
+ if (scb->expect == 0) {
+ /* Complete frame */
+
+ data->rx_skb[queue] = NULL;
+
+ bt_cb(skb)->pkt_type = scb->type;
+ hci_recv_frame(skb);
+ }
+
+ count -= len; buf += len;
+ }
+
+ return 0;
+}
+
+static void bpa10x_tx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+
+ BT_DBG("%s urb %p status %d count %d", hdev->name,
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ goto done;
+
+ if (!urb->status)
+ hdev->stat.byte_tx += urb->transfer_buffer_length;
+ else
+ hdev->stat.err_tx++;
+
+done:
+ kfree(urb->setup_packet);
+
+ kfree_skb(skb);
+}
+
+static void bpa10x_rx_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = urb->context;
+ struct bpa10x_data *data = hdev->driver_data;
+ int err;
+
+ BT_DBG("%s urb %p status %d count %d", hdev->name,
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return;
+
+ if (urb->status == 0) {
+ if (bpa10x_recv(hdev, usb_pipebulk(urb->pipe),
+ urb->transfer_buffer,
+ urb->actual_length) < 0) {
+ BT_ERR("%s corrupted event packet", hdev->name);
+ hdev->stat.err_rx++;
+ }
+ }
+
+ usb_anchor_urb(urb, &data->rx_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ BT_ERR("%s urb %p failed to resubmit (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static inline int bpa10x_submit_intr_urb(struct hci_dev *hdev)
+{
+ struct bpa10x_data *data = hdev->driver_data;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size = 16;
+
+ BT_DBG("%s", hdev->name);
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvintpipe(data->udev, 0x81);
+
+ usb_fill_int_urb(urb, data->udev, pipe, buf, size,
+ bpa10x_rx_complete, hdev, 1);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_anchor_urb(urb, &data->rx_anchor);
+
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err < 0) {
+ BT_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static inline int bpa10x_submit_bulk_urb(struct hci_dev *hdev)
+{
+ struct bpa10x_data *data = hdev->driver_data;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size = 64;
+
+ BT_DBG("%s", hdev->name);
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvbulkpipe(data->udev, 0x82);
+
+ usb_fill_bulk_urb(urb, data->udev, pipe,
+ buf, size, bpa10x_rx_complete, hdev);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_anchor_urb(urb, &data->rx_anchor);
+
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err < 0) {
+ BT_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static int bpa10x_open(struct hci_dev *hdev)
+{
+ struct bpa10x_data *data = hdev->driver_data;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ err = bpa10x_submit_intr_urb(hdev);
+ if (err < 0)
+ goto error;
+
+ err = bpa10x_submit_bulk_urb(hdev);
+ if (err < 0)
+ goto error;
+
+ return 0;
+
+error:
+ usb_kill_anchored_urbs(&data->rx_anchor);
+
+ clear_bit(HCI_RUNNING, &hdev->flags);
+
+ return err;
+}
+
+static int bpa10x_close(struct hci_dev *hdev)
+{
+ struct bpa10x_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ usb_kill_anchored_urbs(&data->rx_anchor);
+
+ return 0;
+}
+
+static int bpa10x_flush(struct hci_dev *hdev)
+{
+ struct bpa10x_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ usb_kill_anchored_urbs(&data->tx_anchor);
+
+ return 0;
+}
+
+static int bpa10x_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct bpa10x_data *data = hdev->driver_data;
+ struct usb_ctrlrequest *dr;
+ struct urb *urb;
+ unsigned int pipe;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+
+ /* Prepend skb with frame type */
+ *skb_push(skb, 1) = bt_cb(skb)->pkt_type;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
+ if (!dr) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ dr->bRequestType = USB_TYPE_VENDOR;
+ dr->bRequest = 0;
+ dr->wIndex = 0;
+ dr->wValue = 0;
+ dr->wLength = __cpu_to_le16(skb->len);
+
+ pipe = usb_sndctrlpipe(data->udev, 0x00);
+
+ usb_fill_control_urb(urb, data->udev, pipe, (void *) dr,
+ skb->data, skb->len, bpa10x_tx_complete, skb);
+
+ hdev->stat.cmd_tx++;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ pipe = usb_sndbulkpipe(data->udev, 0x02);
+
+ usb_fill_bulk_urb(urb, data->udev, pipe,
+ skb->data, skb->len, bpa10x_tx_complete, skb);
+
+ hdev->stat.acl_tx++;
+ break;
+
+ case HCI_SCODATA_PKT:
+ pipe = usb_sndbulkpipe(data->udev, 0x02);
+
+ usb_fill_bulk_urb(urb, data->udev, pipe,
+ skb->data, skb->len, bpa10x_tx_complete, skb);
+
+ hdev->stat.sco_tx++;
+ break;
+
+ default:
+ usb_free_urb(urb);
+ return -EILSEQ;
+ }
+
+ usb_anchor_urb(urb, &data->tx_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ BT_ERR("%s urb %p submission failed", hdev->name, urb);
+ kfree(urb->setup_packet);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return 0;
+}
+
+static void bpa10x_destruct(struct hci_dev *hdev)
+{
+ struct bpa10x_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ kfree_skb(data->rx_skb[0]);
+ kfree_skb(data->rx_skb[1]);
+ kfree(data);
+}
+
+static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct bpa10x_data *data;
+ struct hci_dev *hdev;
+ int err;
+
+ BT_DBG("intf %p id %p", intf, id);
+
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->udev = interface_to_usbdev(intf);
+
+ init_usb_anchor(&data->tx_anchor);
+ init_usb_anchor(&data->rx_anchor);
+
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ hdev->bus = HCI_USB;
+ hdev->driver_data = data;
+
+ data->hdev = hdev;
+
+ SET_HCIDEV_DEV(hdev, &intf->dev);
+
+ hdev->open = bpa10x_open;
+ hdev->close = bpa10x_close;
+ hdev->flush = bpa10x_flush;
+ hdev->send = bpa10x_send_frame;
+ hdev->destruct = bpa10x_destruct;
+
+ hdev->owner = THIS_MODULE;
+
+ set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
+
+ err = hci_register_dev(hdev);
+ if (err < 0) {
+ hci_free_dev(hdev);
+ kfree(data);
+ return err;
+ }
+
+ usb_set_intfdata(intf, data);
+
+ return 0;
+}
+
+static void bpa10x_disconnect(struct usb_interface *intf)
+{
+ struct bpa10x_data *data = usb_get_intfdata(intf);
+
+ BT_DBG("intf %p", intf);
+
+ if (!data)
+ return;
+
+ usb_set_intfdata(intf, NULL);
+
+ hci_unregister_dev(data->hdev);
+
+ hci_free_dev(data->hdev);
+}
+
+static struct usb_driver bpa10x_driver = {
+ .name = "bpa10x",
+ .probe = bpa10x_probe,
+ .disconnect = bpa10x_disconnect,
+ .id_table = bpa10x_table,
+};
+
+static int __init bpa10x_init(void)
+{
+ BT_INFO("Digianswer Bluetooth USB driver ver %s", VERSION);
+
+ return usb_register(&bpa10x_driver);
+}
+
+static void __exit bpa10x_exit(void)
+{
+ usb_deregister(&bpa10x_driver);
+}
+
+module_init(bpa10x_init);
+module_exit(bpa10x_exit);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Digianswer Bluetooth USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
new file mode 100644
index 0000000..27f4f4f
--- /dev/null
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -0,0 +1,893 @@
+/*
+ *
+ * Driver for the 3Com Bluetooth PCMCIA card
+ *
+ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
+ * Jose Orlando Pereira <jop@di.uminho.pt>
+ *
+ *
+ * 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;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/bitops.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth driver for the 3Com Bluetooth PCMCIA card");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("BT3CPCC.bin");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct bt3c_info_t {
+ struct pcmcia_device *p_dev;
+
+ struct hci_dev *hdev;
+
+ spinlock_t lock; /* For serializing operations */
+
+ struct sk_buff_head txq;
+ unsigned long tx_state;
+
+ unsigned long rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+} bt3c_info_t;
+
+
+static int bt3c_config(struct pcmcia_device *link);
+static void bt3c_release(struct pcmcia_device *link);
+
+static void bt3c_detach(struct pcmcia_device *p_dev);
+
+
+/* Transmit states */
+#define XMIT_SENDING 1
+#define XMIT_WAKEUP 2
+#define XMIT_WAITING 8
+
+/* Receiver states */
+#define RECV_WAIT_PACKET_TYPE 0
+#define RECV_WAIT_EVENT_HEADER 1
+#define RECV_WAIT_ACL_HEADER 2
+#define RECV_WAIT_SCO_HEADER 3
+#define RECV_WAIT_DATA 4
+
+
+
+/* ======================== Special I/O functions ======================== */
+
+
+#define DATA_L 0
+#define DATA_H 1
+#define ADDR_L 2
+#define ADDR_H 3
+#define CONTROL 4
+
+
+static inline void bt3c_address(unsigned int iobase, unsigned short addr)
+{
+ outb(addr & 0xff, iobase + ADDR_L);
+ outb((addr >> 8) & 0xff, iobase + ADDR_H);
+}
+
+
+static inline void bt3c_put(unsigned int iobase, unsigned short value)
+{
+ outb(value & 0xff, iobase + DATA_L);
+ outb((value >> 8) & 0xff, iobase + DATA_H);
+}
+
+
+static inline void bt3c_io_write(unsigned int iobase, unsigned short addr, unsigned short value)
+{
+ bt3c_address(iobase, addr);
+ bt3c_put(iobase, value);
+}
+
+
+static inline unsigned short bt3c_get(unsigned int iobase)
+{
+ unsigned short value = inb(iobase + DATA_L);
+
+ value |= inb(iobase + DATA_H) << 8;
+
+ return value;
+}
+
+
+static inline unsigned short bt3c_read(unsigned int iobase, unsigned short addr)
+{
+ bt3c_address(iobase, addr);
+
+ return bt3c_get(iobase);
+}
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
+{
+ int actual = 0;
+
+ bt3c_address(iobase, 0x7080);
+
+ /* Fill FIFO with current frame */
+ while (actual < len) {
+ /* Transmit next byte */
+ bt3c_put(iobase, buf[actual]);
+ actual++;
+ }
+
+ bt3c_io_write(iobase, 0x7005, actual);
+
+ return actual;
+}
+
+
+static void bt3c_write_wakeup(bt3c_info_t *info)
+{
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state)))
+ return;
+
+ do {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ register unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+ register unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+ register struct sk_buff *skb;
+ register int len;
+
+ if (!pcmcia_dev_present(info->p_dev))
+ break;
+
+
+ if (!(skb = skb_dequeue(&(info->txq)))) {
+ clear_bit(XMIT_SENDING, &(info->tx_state));
+ break;
+ }
+
+ /* Send frame */
+ len = bt3c_write(iobase, 256, skb->data, skb->len);
+
+ if (len != skb->len) {
+ BT_ERR("Very strange");
+ }
+
+ kfree_skb(skb);
+
+ info->hdev->stat.byte_tx += len;
+
+ } while (0);
+}
+
+
+static void bt3c_receive(bt3c_info_t *info)
+{
+ unsigned int iobase;
+ int size = 0, avail;
+
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ iobase = info->p_dev->resource[0]->start;
+#else
+ iobase = info->p_dev->io.BasePort1;
+#endif
+
+ avail = bt3c_read(iobase, 0x7006);
+ //printk("bt3c_cs: receiving %d bytes\n", avail);
+
+ bt3c_address(iobase, 0x7480);
+ while (size < avail) {
+ size++;
+ info->hdev->stat.byte_rx++;
+
+ /* Allocate packet */
+ if (info->rx_skb == NULL) {
+ info->rx_state = RECV_WAIT_PACKET_TYPE;
+ info->rx_count = 0;
+ if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate mem for new packet");
+ return;
+ }
+ }
+
+
+ if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
+
+ info->rx_skb->dev = (void *) info->hdev;
+ bt_cb(info->rx_skb)->pkt_type = inb(iobase + DATA_L);
+ inb(iobase + DATA_H);
+ //printk("bt3c: PACKET_TYPE=%02x\n", bt_cb(info->rx_skb)->pkt_type);
+
+ switch (bt_cb(info->rx_skb)->pkt_type) {
+
+ case HCI_EVENT_PKT:
+ info->rx_state = RECV_WAIT_EVENT_HEADER;
+ info->rx_count = HCI_EVENT_HDR_SIZE;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ info->rx_state = RECV_WAIT_ACL_HEADER;
+ info->rx_count = HCI_ACL_HDR_SIZE;
+ break;
+
+ case HCI_SCODATA_PKT:
+ info->rx_state = RECV_WAIT_SCO_HEADER;
+ info->rx_count = HCI_SCO_HDR_SIZE;
+ break;
+
+ default:
+ /* Unknown packet */
+ BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+ info->hdev->stat.err_rx++;
+ clear_bit(HCI_RUNNING, &(info->hdev->flags));
+
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+
+ }
+
+ } else {
+
+ __u8 x = inb(iobase + DATA_L);
+
+ *skb_put(info->rx_skb, 1) = x;
+ inb(iobase + DATA_H);
+ info->rx_count--;
+
+ if (info->rx_count == 0) {
+
+ int dlen;
+ struct hci_event_hdr *eh;
+ struct hci_acl_hdr *ah;
+ struct hci_sco_hdr *sh;
+
+ switch (info->rx_state) {
+
+ case RECV_WAIT_EVENT_HEADER:
+ eh = hci_event_hdr(info->rx_skb);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = eh->plen;
+ break;
+
+ case RECV_WAIT_ACL_HEADER:
+ ah = hci_acl_hdr(info->rx_skb);
+ dlen = __le16_to_cpu(ah->dlen);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = dlen;
+ break;
+
+ case RECV_WAIT_SCO_HEADER:
+ sh = hci_sco_hdr(info->rx_skb);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = sh->dlen;
+ break;
+
+ case RECV_WAIT_DATA:
+ hci_recv_frame(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ bt3c_io_write(iobase, 0x7006, 0x0000);
+}
+
+
+static irqreturn_t bt3c_interrupt(int irq, void *dev_inst)
+{
+ bt3c_info_t *info = dev_inst;
+ unsigned int iobase;
+ int iir;
+ irqreturn_t r = IRQ_NONE;
+
+ if (!info || !info->hdev)
+ /* our irq handler is shared */
+ return IRQ_NONE;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ iobase = info->p_dev->resource[0]->start;
+#else
+ iobase = info->p_dev->io.BasePort1;
+#endif
+
+ spin_lock(&(info->lock));
+
+ iir = inb(iobase + CONTROL);
+ if (iir & 0x80) {
+ int stat = bt3c_read(iobase, 0x7001);
+
+ if ((stat & 0xff) == 0x7f) {
+ BT_ERR("Very strange (stat=0x%04x)", stat);
+ } else if ((stat & 0xff) != 0xff) {
+ if (stat & 0x0020) {
+ int status = bt3c_read(iobase, 0x7002) & 0x10;
+ BT_INFO("%s: Antenna %s", info->hdev->name,
+ status ? "out" : "in");
+ }
+ if (stat & 0x0001)
+ bt3c_receive(info);
+ if (stat & 0x0002) {
+ //BT_ERR("Ack (stat=0x%04x)", stat);
+ clear_bit(XMIT_SENDING, &(info->tx_state));
+ bt3c_write_wakeup(info);
+ }
+
+ bt3c_io_write(iobase, 0x7001, 0x0000);
+
+ outb(iir, iobase + CONTROL);
+ }
+ r = IRQ_HANDLED;
+ }
+
+ spin_unlock(&(info->lock));
+
+ return r;
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int bt3c_hci_flush(struct hci_dev *hdev)
+{
+ bt3c_info_t *info = (bt3c_info_t *)(hdev->driver_data);
+
+ /* Drop TX queue */
+ skb_queue_purge(&(info->txq));
+
+ return 0;
+}
+
+
+static int bt3c_hci_open(struct hci_dev *hdev)
+{
+ set_bit(HCI_RUNNING, &(hdev->flags));
+
+ return 0;
+}
+
+
+static int bt3c_hci_close(struct hci_dev *hdev)
+{
+ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+ return 0;
+
+ bt3c_hci_flush(hdev);
+
+ return 0;
+}
+
+
+static int bt3c_hci_send_frame(struct sk_buff *skb)
+{
+ bt3c_info_t *info;
+ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+ unsigned long flags;
+
+ if (!hdev) {
+ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+ info = (bt3c_info_t *) (hdev->driver_data);
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+ };
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ skb_queue_tail(&(info->txq), skb);
+
+ spin_lock_irqsave(&(info->lock), flags);
+
+ bt3c_write_wakeup(info);
+
+ spin_unlock_irqrestore(&(info->lock), flags);
+
+ return 0;
+}
+
+
+static void bt3c_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int bt3c_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+static int bt3c_load_firmware(bt3c_info_t *info, const unsigned char *firmware,
+ int count)
+{
+ char *ptr = (char *) firmware;
+ char b[9];
+ unsigned int iobase, size, addr, fcs, tmp;
+ int i, err = 0;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ iobase = info->p_dev->resource[0]->start;
+#else
+ iobase = info->p_dev->io.BasePort1;
+#endif
+
+ /* Reset */
+ bt3c_io_write(iobase, 0x8040, 0x0404);
+ bt3c_io_write(iobase, 0x8040, 0x0400);
+
+ udelay(1);
+
+ bt3c_io_write(iobase, 0x8040, 0x0404);
+
+ udelay(17);
+
+ /* Load */
+ while (count) {
+ if (ptr[0] != 'S') {
+ BT_ERR("Bad address in firmware");
+ err = -EFAULT;
+ goto error;
+ }
+
+ memset(b, 0, sizeof(b));
+ memcpy(b, ptr + 2, 2);
+ size = simple_strtoul(b, NULL, 16);
+
+ memset(b, 0, sizeof(b));
+ memcpy(b, ptr + 4, 8);
+ addr = simple_strtoul(b, NULL, 16);
+
+ memset(b, 0, sizeof(b));
+ memcpy(b, ptr + (size * 2) + 2, 2);
+ fcs = simple_strtoul(b, NULL, 16);
+
+ memset(b, 0, sizeof(b));
+ for (tmp = 0, i = 0; i < size; i++) {
+ memcpy(b, ptr + (i * 2) + 2, 2);
+ tmp += simple_strtol(b, NULL, 16);
+ }
+
+ if (((tmp + fcs) & 0xff) != 0xff) {
+ BT_ERR("Checksum error in firmware");
+ err = -EILSEQ;
+ goto error;
+ }
+
+ if (ptr[1] == '3') {
+ bt3c_address(iobase, addr);
+
+ memset(b, 0, sizeof(b));
+ for (i = 0; i < (size - 4) / 2; i++) {
+ memcpy(b, ptr + (i * 4) + 12, 4);
+ tmp = simple_strtoul(b, NULL, 16);
+ bt3c_put(iobase, tmp);
+ }
+ }
+
+ ptr += (size * 2) + 6;
+ count -= (size * 2) + 6;
+ }
+
+ udelay(17);
+
+ /* Boot */
+ bt3c_address(iobase, 0x3000);
+ outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL);
+
+error:
+ udelay(17);
+
+ /* Clear */
+ bt3c_io_write(iobase, 0x7006, 0x0000);
+ bt3c_io_write(iobase, 0x7005, 0x0000);
+ bt3c_io_write(iobase, 0x7001, 0x0000);
+
+ return err;
+}
+
+
+static int bt3c_open(bt3c_info_t *info)
+{
+ const struct firmware *firmware;
+ struct hci_dev *hdev;
+ int err;
+
+ spin_lock_init(&(info->lock));
+
+ skb_queue_head_init(&(info->txq));
+
+ info->rx_state = RECV_WAIT_PACKET_TYPE;
+ info->rx_count = 0;
+ info->rx_skb = NULL;
+
+ /* Initialize HCI device */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BT_ERR("Can't allocate HCI device");
+ return -ENOMEM;
+ }
+
+ info->hdev = hdev;
+
+ hdev->bus = HCI_PCCARD;
+ hdev->driver_data = info;
+ SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
+
+ hdev->open = bt3c_hci_open;
+ hdev->close = bt3c_hci_close;
+ hdev->flush = bt3c_hci_flush;
+ hdev->send = bt3c_hci_send_frame;
+ hdev->destruct = bt3c_hci_destruct;
+ hdev->ioctl = bt3c_hci_ioctl;
+
+ hdev->owner = THIS_MODULE;
+
+ /* Load firmware */
+ err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev);
+ if (err < 0) {
+ BT_ERR("Firmware request failed");
+ goto error;
+ }
+
+ err = bt3c_load_firmware(info, firmware->data, firmware->size);
+
+ release_firmware(firmware);
+
+ if (err < 0) {
+ BT_ERR("Firmware loading failed");
+ goto error;
+ }
+
+ /* Timeout before it is safe to send the first HCI packet */
+ msleep(1000);
+
+ /* Register HCI device */
+ err = hci_register_dev(hdev);
+ if (err < 0) {
+ BT_ERR("Can't register HCI device");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ info->hdev = NULL;
+ hci_free_dev(hdev);
+ return err;
+}
+
+
+static int bt3c_close(bt3c_info_t *info)
+{
+ struct hci_dev *hdev = info->hdev;
+
+ if (!hdev)
+ return -ENODEV;
+
+ bt3c_hci_close(hdev);
+
+ hci_unregister_dev(hdev);
+ hci_free_dev(hdev);
+
+ return 0;
+}
+
+static int bt3c_probe(struct pcmcia_device *link)
+{
+ bt3c_info_t *info;
+
+ /* Create new info device */
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->p_dev = link;
+ link->priv = info;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37))
+ link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP |
+ CONF_AUTO_SET_IO;
+#else
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+ link->resource[0]->end = 8;
+#else
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.NumPorts1= 8;
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35))
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+
+ link->irq.Handler = bt3c_interrupt;
+#endif
+
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+#endif
+
+ return bt3c_config(link);
+}
+
+
+static void bt3c_detach(struct pcmcia_device *link)
+{
+ bt3c_info_t *info = link->priv;
+
+ bt3c_release(link);
+ kfree(info);
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37))
+static int bt3c_check_config(struct pcmcia_device *p_dev, void *priv_data)
+{
+ int *try = priv_data;
+
+ if (try == 0)
+ p_dev->io_lines = 16;
+
+ if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0))
+ return -EINVAL;
+
+ p_dev->resource[0]->end = 8;
+ p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+ p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+
+ return pcmcia_request_io(p_dev);
+}
+
+static int bt3c_check_config_notpicky(struct pcmcia_device *p_dev,
+ void *priv_data)
+{
+ static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
+ int j;
+
+ if (p_dev->io_lines > 3)
+ return -ENODEV;
+
+ p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+ p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+ p_dev->resource[0]->end = 8;
+
+ for (j = 0; j < 5; j++) {
+ p_dev->resource[0]->start = base[j];
+ p_dev->io_lines = base[j] ? 16 : 3;
+ if (!pcmcia_request_io(p_dev))
+ return 0;
+ }
+ return -ENODEV;
+}
+#else
+static int bt3c_check_config(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ unsigned long try = (unsigned long) priv_data;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ p_dev->io_lines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK;
+#endif
+
+ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) &&
+ (cf->io.win[0].base != 0)) {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ p_dev->resource[0]->start = cf->io.win[0].base;
+ if (!pcmcia_request_io(p_dev))
+#else
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.IOAddrLines = (try == 0) ? 16 :
+ cf->io.flags & CISTPL_IO_LINES_MASK;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+#endif
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static int bt3c_check_config_notpicky(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
+ int j;
+
+ if ((cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
+ for (j = 0; j < 5; j++) {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ p_dev->resource[0]->start = base[j];
+ p_dev->io_lines = base[j] ? 16 : 3;
+ if (!pcmcia_request_io(p_dev))
+#else
+ p_dev->io.BasePort1 = base[j];
+ p_dev->io.IOAddrLines = base[j] ? 16 : 3;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+#endif
+ return 0;
+ }
+ }
+ return -ENODEV;
+}
+#endif
+
+static int bt3c_config(struct pcmcia_device *link)
+{
+ bt3c_info_t *info = link->priv;
+ int i;
+ unsigned long try;
+
+ /* First pass: look for a config entry that looks normal.
+ Two tries: without IO aliases, then with aliases */
+ for (try = 0; try < 2; try++)
+ if (!pcmcia_loop_config(link, bt3c_check_config, (void *) try))
+ goto found_port;
+
+ /* Second pass: try to find an entry that isn't picky about
+ its base address, then try to grab any standard serial port
+ address, and finally try to get any free port. */
+ if (!pcmcia_loop_config(link, bt3c_check_config_notpicky, NULL))
+ goto found_port;
+
+ BT_ERR("No usable port range found");
+ goto failed;
+
+found_port:
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ i = pcmcia_request_irq(link, &bt3c_interrupt);
+ if (i != 0)
+ goto failed;
+#else
+ i = pcmcia_request_irq(link, &link->irq);
+ if (i != 0)
+ link->irq.AssignedIRQ = 0;
+#endif
+
+ i = pcmcia_enable_device(link);
+ if (i != 0)
+ goto failed;
+
+ if (bt3c_open(info) != 0)
+ goto failed;
+
+ return 0;
+
+failed:
+ bt3c_release(link);
+ return -ENODEV;
+}
+
+
+static void bt3c_release(struct pcmcia_device *link)
+{
+ bt3c_info_t *info = link->priv;
+
+ bt3c_close(info);
+
+ pcmcia_disable_device(link);
+}
+
+
+static const struct pcmcia_device_id bt3c_ids[] = {
+ PCMCIA_DEVICE_PROD_ID13("3COM", "Bluetooth PC Card", 0xefce0a31, 0xd4ce9b02),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, bt3c_ids);
+
+static struct pcmcia_driver bt3c_driver = {
+ .owner = THIS_MODULE,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37))
+ .name = "bt3c_cs",
+#else
+ .drv = {
+ .name = "bt3c_cs",
+ },
+#endif
+ .probe = bt3c_probe,
+ .remove = bt3c_detach,
+ .id_table = bt3c_ids,
+};
+
+static int __init init_bt3c_cs(void)
+{
+ return pcmcia_register_driver(&bt3c_driver);
+}
+
+
+static void __exit exit_bt3c_cs(void)
+{
+ pcmcia_unregister_driver(&bt3c_driver);
+}
+
+module_init(init_bt3c_cs);
+module_exit(exit_bt3c_cs);
diff --git a/drivers/bluetooth/btmrvl_debugfs.c b/drivers/bluetooth/btmrvl_debugfs.c
new file mode 100644
index 0000000..8ecf4c6
--- /dev/null
+++ b/drivers/bluetooth/btmrvl_debugfs.c
@@ -0,0 +1,454 @@
+/**
+ * Marvell Bluetooth driver: debugfs related functions
+ *
+ * Copyright (C) 2009, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btmrvl_drv.h"
+
+struct btmrvl_debugfs_data {
+ struct dentry *config_dir;
+ struct dentry *status_dir;
+
+ /* config */
+ struct dentry *psmode;
+ struct dentry *pscmd;
+ struct dentry *hsmode;
+ struct dentry *hscmd;
+ struct dentry *gpiogap;
+ struct dentry *hscfgcmd;
+
+ /* status */
+ struct dentry *curpsmode;
+ struct dentry *hsstate;
+ struct dentry *psstate;
+ struct dentry *txdnldready;
+};
+
+static int btmrvl_open_generic(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t btmrvl_hscfgcmd_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ long result, ret;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ ret = strict_strtol(buf, 10, &result);
+ if (ret)
+ return ret;
+
+ priv->btmrvl_dev.hscfgcmd = result;
+
+ if (priv->btmrvl_dev.hscfgcmd) {
+ btmrvl_prepare_command(priv);
+ wake_up_interruptible(&priv->main_thread.wait_q);
+ }
+
+ return count;
+}
+
+static ssize_t btmrvl_hscfgcmd_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ int ret;
+
+ ret = snprintf(buf, sizeof(buf) - 1, "%d\n",
+ priv->btmrvl_dev.hscfgcmd);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_hscfgcmd_fops = {
+ .read = btmrvl_hscfgcmd_read,
+ .write = btmrvl_hscfgcmd_write,
+ .open = btmrvl_open_generic,
+ .llseek = default_llseek,
+};
+
+static ssize_t btmrvl_psmode_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ long result, ret;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ ret = strict_strtol(buf, 10, &result);
+ if (ret)
+ return ret;
+
+ priv->btmrvl_dev.psmode = result;
+
+ return count;
+}
+
+static ssize_t btmrvl_psmode_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ int ret;
+
+ ret = snprintf(buf, sizeof(buf) - 1, "%d\n",
+ priv->btmrvl_dev.psmode);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_psmode_fops = {
+ .read = btmrvl_psmode_read,
+ .write = btmrvl_psmode_write,
+ .open = btmrvl_open_generic,
+ .llseek = default_llseek,
+};
+
+static ssize_t btmrvl_pscmd_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ long result, ret;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ ret = strict_strtol(buf, 10, &result);
+ if (ret)
+ return ret;
+
+ priv->btmrvl_dev.pscmd = result;
+
+ if (priv->btmrvl_dev.pscmd) {
+ btmrvl_prepare_command(priv);
+ wake_up_interruptible(&priv->main_thread.wait_q);
+ }
+
+ return count;
+
+}
+
+static ssize_t btmrvl_pscmd_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ int ret;
+
+ ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.pscmd);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_pscmd_fops = {
+ .read = btmrvl_pscmd_read,
+ .write = btmrvl_pscmd_write,
+ .open = btmrvl_open_generic,
+ .llseek = default_llseek,
+};
+
+static ssize_t btmrvl_gpiogap_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ long result, ret;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ ret = strict_strtol(buf, 16, &result);
+ if (ret)
+ return ret;
+
+ priv->btmrvl_dev.gpio_gap = result;
+
+ return count;
+}
+
+static ssize_t btmrvl_gpiogap_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ int ret;
+
+ ret = snprintf(buf, sizeof(buf) - 1, "0x%x\n",
+ priv->btmrvl_dev.gpio_gap);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_gpiogap_fops = {
+ .read = btmrvl_gpiogap_read,
+ .write = btmrvl_gpiogap_write,
+ .open = btmrvl_open_generic,
+ .llseek = default_llseek,
+};
+
+static ssize_t btmrvl_hscmd_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ long result, ret;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ ret = strict_strtol(buf, 10, &result);
+ if (ret)
+ return ret;
+
+ priv->btmrvl_dev.hscmd = result;
+ if (priv->btmrvl_dev.hscmd) {
+ btmrvl_prepare_command(priv);
+ wake_up_interruptible(&priv->main_thread.wait_q);
+ }
+
+ return count;
+}
+
+static ssize_t btmrvl_hscmd_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ int ret;
+
+ ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.hscmd);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_hscmd_fops = {
+ .read = btmrvl_hscmd_read,
+ .write = btmrvl_hscmd_write,
+ .open = btmrvl_open_generic,
+ .llseek = default_llseek,
+};
+
+static ssize_t btmrvl_hsmode_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ long result, ret;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ ret = strict_strtol(buf, 10, &result);
+ if (ret)
+ return ret;
+
+ priv->btmrvl_dev.hsmode = result;
+
+ return count;
+}
+
+static ssize_t btmrvl_hsmode_read(struct file *file, char __user * userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ int ret;
+
+ ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.hsmode);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_hsmode_fops = {
+ .read = btmrvl_hsmode_read,
+ .write = btmrvl_hsmode_write,
+ .open = btmrvl_open_generic,
+ .llseek = default_llseek,
+};
+
+static ssize_t btmrvl_curpsmode_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ int ret;
+
+ ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->psmode);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_curpsmode_fops = {
+ .read = btmrvl_curpsmode_read,
+ .open = btmrvl_open_generic,
+ .llseek = default_llseek,
+};
+
+static ssize_t btmrvl_psstate_read(struct file *file, char __user * userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ int ret;
+
+ ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->ps_state);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_psstate_fops = {
+ .read = btmrvl_psstate_read,
+ .open = btmrvl_open_generic,
+ .llseek = default_llseek,
+};
+
+static ssize_t btmrvl_hsstate_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ int ret;
+
+ ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->hs_state);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_hsstate_fops = {
+ .read = btmrvl_hsstate_read,
+ .open = btmrvl_open_generic,
+ .llseek = default_llseek,
+};
+
+static ssize_t btmrvl_txdnldready_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ int ret;
+
+ ret = snprintf(buf, sizeof(buf) - 1, "%d\n",
+ priv->btmrvl_dev.tx_dnld_rdy);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_txdnldready_fops = {
+ .read = btmrvl_txdnldready_read,
+ .open = btmrvl_open_generic,
+ .llseek = default_llseek,
+};
+
+void btmrvl_debugfs_init(struct hci_dev *hdev)
+{
+ struct btmrvl_private *priv = hdev->driver_data;
+ struct btmrvl_debugfs_data *dbg;
+
+ if (!hdev->debugfs)
+ return;
+
+ dbg = kzalloc(sizeof(*dbg), GFP_KERNEL);
+ priv->debugfs_data = dbg;
+
+ if (!dbg) {
+ BT_ERR("Can not allocate memory for btmrvl_debugfs_data.");
+ return;
+ }
+
+ dbg->config_dir = debugfs_create_dir("config", hdev->debugfs);
+
+ dbg->psmode = debugfs_create_file("psmode", 0644, dbg->config_dir,
+ hdev->driver_data, &btmrvl_psmode_fops);
+ dbg->pscmd = debugfs_create_file("pscmd", 0644, dbg->config_dir,
+ hdev->driver_data, &btmrvl_pscmd_fops);
+ dbg->gpiogap = debugfs_create_file("gpiogap", 0644, dbg->config_dir,
+ hdev->driver_data, &btmrvl_gpiogap_fops);
+ dbg->hsmode = debugfs_create_file("hsmode", 0644, dbg->config_dir,
+ hdev->driver_data, &btmrvl_hsmode_fops);
+ dbg->hscmd = debugfs_create_file("hscmd", 0644, dbg->config_dir,
+ hdev->driver_data, &btmrvl_hscmd_fops);
+ dbg->hscfgcmd = debugfs_create_file("hscfgcmd", 0644, dbg->config_dir,
+ hdev->driver_data, &btmrvl_hscfgcmd_fops);
+
+ dbg->status_dir = debugfs_create_dir("status", hdev->debugfs);
+ dbg->curpsmode = debugfs_create_file("curpsmode", 0444,
+ dbg->status_dir,
+ hdev->driver_data,
+ &btmrvl_curpsmode_fops);
+ dbg->psstate = debugfs_create_file("psstate", 0444, dbg->status_dir,
+ hdev->driver_data, &btmrvl_psstate_fops);
+ dbg->hsstate = debugfs_create_file("hsstate", 0444, dbg->status_dir,
+ hdev->driver_data, &btmrvl_hsstate_fops);
+ dbg->txdnldready = debugfs_create_file("txdnldready", 0444,
+ dbg->status_dir,
+ hdev->driver_data,
+ &btmrvl_txdnldready_fops);
+}
+
+void btmrvl_debugfs_remove(struct hci_dev *hdev)
+{
+ struct btmrvl_private *priv = hdev->driver_data;
+ struct btmrvl_debugfs_data *dbg = priv->debugfs_data;
+
+ if (!dbg)
+ return;
+
+ debugfs_remove(dbg->psmode);
+ debugfs_remove(dbg->pscmd);
+ debugfs_remove(dbg->gpiogap);
+ debugfs_remove(dbg->hsmode);
+ debugfs_remove(dbg->hscmd);
+ debugfs_remove(dbg->hscfgcmd);
+ debugfs_remove(dbg->config_dir);
+
+ debugfs_remove(dbg->curpsmode);
+ debugfs_remove(dbg->psstate);
+ debugfs_remove(dbg->hsstate);
+ debugfs_remove(dbg->txdnldready);
+ debugfs_remove(dbg->status_dir);
+
+ kfree(dbg);
+}
diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h
new file mode 100644
index 0000000..90bda50
--- /dev/null
+++ b/drivers/bluetooth/btmrvl_drv.h
@@ -0,0 +1,148 @@
+/*
+ * Marvell Bluetooth driver: global definitions & declarations
+ *
+ * Copyright (C) 2009, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ *
+ */
+
+#include <linux/kthread.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <net/bluetooth/bluetooth.h>
+
+#define BTM_HEADER_LEN 4
+#define BTM_UPLD_SIZE 2312
+
+/* Time to wait until Host Sleep state change in millisecond */
+#define WAIT_UNTIL_HS_STATE_CHANGED 5000
+/* Time to wait for command response in millisecond */
+#define WAIT_UNTIL_CMD_RESP 5000
+
+struct btmrvl_thread {
+ struct task_struct *task;
+ wait_queue_head_t wait_q;
+ void *priv;
+};
+
+struct btmrvl_device {
+ void *card;
+ struct hci_dev *hcidev;
+
+ u8 dev_type;
+
+ u8 tx_dnld_rdy;
+
+ u8 psmode;
+ u8 pscmd;
+ u8 hsmode;
+ u8 hscmd;
+
+ /* Low byte is gap, high byte is GPIO */
+ u16 gpio_gap;
+
+ u8 hscfgcmd;
+ u8 sendcmdflag;
+};
+
+struct btmrvl_adapter {
+ u32 int_count;
+ struct sk_buff_head tx_queue;
+ u8 psmode;
+ u8 ps_state;
+ u8 hs_state;
+ u8 wakeup_tries;
+ wait_queue_head_t cmd_wait_q;
+ u8 cmd_complete;
+};
+
+struct btmrvl_private {
+ struct btmrvl_device btmrvl_dev;
+ struct btmrvl_adapter *adapter;
+ struct btmrvl_thread main_thread;
+ int (*hw_host_to_card) (struct btmrvl_private *priv,
+ u8 *payload, u16 nb);
+ int (*hw_wakeup_firmware) (struct btmrvl_private *priv);
+ int (*hw_process_int_status) (struct btmrvl_private *priv);
+ spinlock_t driver_lock; /* spinlock used by driver */
+#ifdef CONFIG_DEBUG_FS
+ void *debugfs_data;
+#endif
+};
+
+#define MRVL_VENDOR_PKT 0xFE
+
+/* Bluetooth commands */
+#define BT_CMD_AUTO_SLEEP_MODE 0x23
+#define BT_CMD_HOST_SLEEP_CONFIG 0x59
+#define BT_CMD_HOST_SLEEP_ENABLE 0x5A
+#define BT_CMD_MODULE_CFG_REQ 0x5B
+
+/* Sub-commands: Module Bringup/Shutdown Request/Response */
+#define MODULE_BRINGUP_REQ 0xF1
+#define MODULE_BROUGHT_UP 0x00
+#define MODULE_ALREADY_UP 0x0C
+
+#define MODULE_SHUTDOWN_REQ 0xF2
+
+#define BT_EVENT_POWER_STATE 0x20
+
+/* Bluetooth Power States */
+#define BT_PS_ENABLE 0x02
+#define BT_PS_DISABLE 0x03
+#define BT_PS_SLEEP 0x01
+
+#define OGF 0x3F
+
+/* Host Sleep states */
+#define HS_ACTIVATED 0x01
+#define HS_DEACTIVATED 0x00
+
+/* Power Save modes */
+#define PS_SLEEP 0x01
+#define PS_AWAKE 0x00
+
+struct btmrvl_cmd {
+ __le16 ocf_ogf;
+ u8 length;
+ u8 data[4];
+} __packed;
+
+struct btmrvl_event {
+ u8 ec; /* event counter */
+ u8 length;
+ u8 data[4];
+} __packed;
+
+/* Prototype of global function */
+
+int btmrvl_register_hdev(struct btmrvl_private *priv);
+struct btmrvl_private *btmrvl_add_card(void *card);
+int btmrvl_remove_card(struct btmrvl_private *priv);
+
+void btmrvl_interrupt(struct btmrvl_private *priv);
+
+void btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb);
+int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb);
+
+int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd);
+int btmrvl_enable_ps(struct btmrvl_private *priv);
+int btmrvl_prepare_command(struct btmrvl_private *priv);
+
+#ifdef CONFIG_DEBUG_FS
+void btmrvl_debugfs_init(struct hci_dev *hdev);
+void btmrvl_debugfs_remove(struct hci_dev *hdev);
+#endif
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c
new file mode 100644
index 0000000..a88a78c
--- /dev/null
+++ b/drivers/bluetooth/btmrvl_main.c
@@ -0,0 +1,662 @@
+/**
+ * Marvell Bluetooth driver
+ *
+ * Copyright (C) 2009, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#include <linux/module.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btmrvl_drv.h"
+
+#define VERSION "1.0"
+
+/*
+ * This function is called by interface specific interrupt handler.
+ * It updates Power Save & Host Sleep states, and wakes up the main
+ * thread.
+ */
+void btmrvl_interrupt(struct btmrvl_private *priv)
+{
+ priv->adapter->ps_state = PS_AWAKE;
+
+ priv->adapter->wakeup_tries = 0;
+
+ priv->adapter->int_count++;
+
+ wake_up_interruptible(&priv->main_thread.wait_q);
+}
+EXPORT_SYMBOL_GPL(btmrvl_interrupt);
+
+void btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb)
+{
+ struct hci_event_hdr *hdr = (void *) skb->data;
+ struct hci_ev_cmd_complete *ec;
+ u16 opcode, ocf;
+
+ if (hdr->evt == HCI_EV_CMD_COMPLETE) {
+ ec = (void *) (skb->data + HCI_EVENT_HDR_SIZE);
+ opcode = __le16_to_cpu(ec->opcode);
+ ocf = hci_opcode_ocf(opcode);
+ if (ocf == BT_CMD_MODULE_CFG_REQ &&
+ priv->btmrvl_dev.sendcmdflag) {
+ priv->btmrvl_dev.sendcmdflag = false;
+ priv->adapter->cmd_complete = true;
+ wake_up_interruptible(&priv->adapter->cmd_wait_q);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(btmrvl_check_evtpkt);
+
+int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
+{
+ struct btmrvl_adapter *adapter = priv->adapter;
+ struct btmrvl_event *event;
+ int ret = 0;
+
+ event = (struct btmrvl_event *) skb->data;
+ if (event->ec != 0xff) {
+ BT_DBG("Not Marvell Event=%x", event->ec);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ switch (event->data[0]) {
+ case BT_CMD_AUTO_SLEEP_MODE:
+ if (!event->data[2]) {
+ if (event->data[1] == BT_PS_ENABLE)
+ adapter->psmode = 1;
+ else
+ adapter->psmode = 0;
+ BT_DBG("PS Mode:%s",
+ (adapter->psmode) ? "Enable" : "Disable");
+ } else {
+ BT_DBG("PS Mode command failed");
+ }
+ break;
+
+ case BT_CMD_HOST_SLEEP_CONFIG:
+ if (!event->data[3])
+ BT_DBG("gpio=%x, gap=%x", event->data[1],
+ event->data[2]);
+ else
+ BT_DBG("HSCFG command failed");
+ break;
+
+ case BT_CMD_HOST_SLEEP_ENABLE:
+ if (!event->data[1]) {
+ adapter->hs_state = HS_ACTIVATED;
+ if (adapter->psmode)
+ adapter->ps_state = PS_SLEEP;
+ wake_up_interruptible(&adapter->cmd_wait_q);
+ BT_DBG("HS ACTIVATED!");
+ } else {
+ BT_DBG("HS Enable failed");
+ }
+ break;
+
+ case BT_CMD_MODULE_CFG_REQ:
+ if (priv->btmrvl_dev.sendcmdflag &&
+ event->data[1] == MODULE_BRINGUP_REQ) {
+ BT_DBG("EVENT:%s",
+ ((event->data[2] == MODULE_BROUGHT_UP) ||
+ (event->data[2] == MODULE_ALREADY_UP)) ?
+ "Bring-up succeed" : "Bring-up failed");
+
+ if (event->length > 3 && event->data[3])
+ priv->btmrvl_dev.dev_type = HCI_AMP;
+ else
+ priv->btmrvl_dev.dev_type = HCI_BREDR;
+
+ BT_DBG("dev_type: %d", priv->btmrvl_dev.dev_type);
+ } else if (priv->btmrvl_dev.sendcmdflag &&
+ event->data[1] == MODULE_SHUTDOWN_REQ) {
+ BT_DBG("EVENT:%s", (event->data[2]) ?
+ "Shutdown failed" : "Shutdown succeed");
+ } else {
+ BT_DBG("BT_CMD_MODULE_CFG_REQ resp for APP");
+ ret = -EINVAL;
+ }
+ break;
+
+ case BT_EVENT_POWER_STATE:
+ if (event->data[1] == BT_PS_SLEEP)
+ adapter->ps_state = PS_SLEEP;
+ BT_DBG("EVENT:%s",
+ (adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE");
+ break;
+
+ default:
+ BT_DBG("Unknown Event=%d", event->data[0]);
+ ret = -EINVAL;
+ break;
+ }
+
+exit:
+ if (!ret)
+ kfree_skb(skb);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(btmrvl_process_event);
+
+int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd)
+{
+ struct sk_buff *skb;
+ struct btmrvl_cmd *cmd;
+ int ret = 0;
+
+ skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
+ if (skb == NULL) {
+ BT_ERR("No free skb");
+ return -ENOMEM;
+ }
+
+ cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
+ cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_MODULE_CFG_REQ));
+ cmd->length = 1;
+ cmd->data[0] = subcmd;
+
+ bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+
+ skb->dev = (void *) priv->btmrvl_dev.hcidev;
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+
+ priv->btmrvl_dev.sendcmdflag = true;
+
+ priv->adapter->cmd_complete = false;
+
+ BT_DBG("Queue module cfg Command");
+
+ wake_up_interruptible(&priv->main_thread.wait_q);
+
+ if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q,
+ priv->adapter->cmd_complete,
+ msecs_to_jiffies(WAIT_UNTIL_CMD_RESP))) {
+ ret = -ETIMEDOUT;
+ BT_ERR("module_cfg_cmd(%x): timeout: %d",
+ subcmd, priv->btmrvl_dev.sendcmdflag);
+ }
+
+ BT_DBG("module cfg Command done");
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd);
+
+int btmrvl_enable_ps(struct btmrvl_private *priv)
+{
+ struct sk_buff *skb;
+ struct btmrvl_cmd *cmd;
+
+ skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
+ if (skb == NULL) {
+ BT_ERR("No free skb");
+ return -ENOMEM;
+ }
+
+ cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
+ cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF,
+ BT_CMD_AUTO_SLEEP_MODE));
+ cmd->length = 1;
+
+ if (priv->btmrvl_dev.psmode)
+ cmd->data[0] = BT_PS_ENABLE;
+ else
+ cmd->data[0] = BT_PS_DISABLE;
+
+ bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+
+ skb->dev = (void *) priv->btmrvl_dev.hcidev;
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+
+ BT_DBG("Queue PSMODE Command:%d", cmd->data[0]);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btmrvl_enable_ps);
+
+static int btmrvl_enable_hs(struct btmrvl_private *priv)
+{
+ struct sk_buff *skb;
+ struct btmrvl_cmd *cmd;
+ int ret = 0;
+
+ skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
+ if (skb == NULL) {
+ BT_ERR("No free skb");
+ return -ENOMEM;
+ }
+
+ cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
+ cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_HOST_SLEEP_ENABLE));
+ cmd->length = 0;
+
+ bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+
+ skb->dev = (void *) priv->btmrvl_dev.hcidev;
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+
+ BT_DBG("Queue hs enable Command");
+
+ wake_up_interruptible(&priv->main_thread.wait_q);
+
+ if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q,
+ priv->adapter->hs_state,
+ msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED))) {
+ ret = -ETIMEDOUT;
+ BT_ERR("timeout: %d, %d,%d", priv->adapter->hs_state,
+ priv->adapter->ps_state,
+ priv->adapter->wakeup_tries);
+ }
+
+ return ret;
+}
+
+int btmrvl_prepare_command(struct btmrvl_private *priv)
+{
+ struct sk_buff *skb = NULL;
+ struct btmrvl_cmd *cmd;
+ int ret = 0;
+
+ if (priv->btmrvl_dev.hscfgcmd) {
+ priv->btmrvl_dev.hscfgcmd = 0;
+
+ skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
+ if (skb == NULL) {
+ BT_ERR("No free skb");
+ return -ENOMEM;
+ }
+
+ cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
+ cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_HOST_SLEEP_CONFIG));
+ cmd->length = 2;
+ cmd->data[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8;
+ cmd->data[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff);
+
+ bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+
+ skb->dev = (void *) priv->btmrvl_dev.hcidev;
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+
+ BT_DBG("Queue HSCFG Command, gpio=0x%x, gap=0x%x",
+ cmd->data[0], cmd->data[1]);
+ }
+
+ if (priv->btmrvl_dev.pscmd) {
+ priv->btmrvl_dev.pscmd = 0;
+ btmrvl_enable_ps(priv);
+ }
+
+ if (priv->btmrvl_dev.hscmd) {
+ priv->btmrvl_dev.hscmd = 0;
+
+ if (priv->btmrvl_dev.hsmode) {
+ ret = btmrvl_enable_hs(priv);
+ } else {
+ ret = priv->hw_wakeup_firmware(priv);
+ priv->adapter->hs_state = HS_DEACTIVATED;
+ }
+ }
+
+ return ret;
+}
+
+static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
+{
+ int ret = 0;
+
+ if (!skb || !skb->data)
+ return -EINVAL;
+
+ if (!skb->len || ((skb->len + BTM_HEADER_LEN) > BTM_UPLD_SIZE)) {
+ BT_ERR("Tx Error: Bad skb length %d : %d",
+ skb->len, BTM_UPLD_SIZE);
+ return -EINVAL;
+ }
+
+ if (skb_headroom(skb) < BTM_HEADER_LEN) {
+ struct sk_buff *tmp = skb;
+
+ skb = skb_realloc_headroom(skb, BTM_HEADER_LEN);
+ if (!skb) {
+ BT_ERR("Tx Error: realloc_headroom failed %d",
+ BTM_HEADER_LEN);
+ skb = tmp;
+ return -EINVAL;
+ }
+
+ kfree_skb(tmp);
+ }
+
+ skb_push(skb, BTM_HEADER_LEN);
+
+ /* header type: byte[3]
+ * HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor
+ * header length: byte[2][1][0]
+ */
+
+ skb->data[0] = (skb->len & 0x0000ff);
+ skb->data[1] = (skb->len & 0x00ff00) >> 8;
+ skb->data[2] = (skb->len & 0xff0000) >> 16;
+ skb->data[3] = bt_cb(skb)->pkt_type;
+
+ if (priv->hw_host_to_card)
+ ret = priv->hw_host_to_card(priv, skb->data, skb->len);
+
+ return ret;
+}
+
+static void btmrvl_init_adapter(struct btmrvl_private *priv)
+{
+ skb_queue_head_init(&priv->adapter->tx_queue);
+
+ priv->adapter->ps_state = PS_AWAKE;
+
+ init_waitqueue_head(&priv->adapter->cmd_wait_q);
+}
+
+static void btmrvl_free_adapter(struct btmrvl_private *priv)
+{
+ skb_queue_purge(&priv->adapter->tx_queue);
+
+ kfree(priv->adapter);
+
+ priv->adapter = NULL;
+}
+
+static int btmrvl_ioctl(struct hci_dev *hdev,
+ unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+static void btmrvl_destruct(struct hci_dev *hdev)
+{
+}
+
+static int btmrvl_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct btmrvl_private *priv = NULL;
+
+ BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len);
+
+ if (!hdev || !hdev->driver_data) {
+ BT_ERR("Frame for unknown HCI device");
+ return -ENODEV;
+ }
+
+ priv = (struct btmrvl_private *) hdev->driver_data;
+ if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+ BT_ERR("Failed testing HCI_RUNING, flags=%lx", hdev->flags);
+ print_hex_dump_bytes("data: ", DUMP_PREFIX_OFFSET,
+ skb->data, skb->len);
+ return -EBUSY;
+ }
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+ }
+
+ skb_queue_tail(&priv->adapter->tx_queue, skb);
+
+ wake_up_interruptible(&priv->main_thread.wait_q);
+
+ return 0;
+}
+
+static int btmrvl_flush(struct hci_dev *hdev)
+{
+ struct btmrvl_private *priv = hdev->driver_data;
+
+ skb_queue_purge(&priv->adapter->tx_queue);
+
+ return 0;
+}
+
+static int btmrvl_close(struct hci_dev *hdev)
+{
+ struct btmrvl_private *priv = hdev->driver_data;
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ skb_queue_purge(&priv->adapter->tx_queue);
+
+ return 0;
+}
+
+static int btmrvl_open(struct hci_dev *hdev)
+{
+ set_bit(HCI_RUNNING, &hdev->flags);
+
+ return 0;
+}
+
+/*
+ * This function handles the event generated by firmware, rx data
+ * received from firmware, and tx data sent from kernel.
+ */
+static int btmrvl_service_main_thread(void *data)
+{
+ struct btmrvl_thread *thread = data;
+ struct btmrvl_private *priv = thread->priv;
+ struct btmrvl_adapter *adapter = priv->adapter;
+ wait_queue_t wait;
+ struct sk_buff *skb;
+ ulong flags;
+
+ init_waitqueue_entry(&wait, current);
+
+ current->flags |= PF_NOFREEZE;
+
+ for (;;) {
+ add_wait_queue(&thread->wait_q, &wait);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (adapter->wakeup_tries ||
+ ((!adapter->int_count) &&
+ (!priv->btmrvl_dev.tx_dnld_rdy ||
+ skb_queue_empty(&adapter->tx_queue)))) {
+ BT_DBG("main_thread is sleeping...");
+ schedule();
+ }
+
+ set_current_state(TASK_RUNNING);
+
+ remove_wait_queue(&thread->wait_q, &wait);
+
+ BT_DBG("main_thread woke up");
+
+ if (kthread_should_stop()) {
+ BT_DBG("main_thread: break from main thread");
+ break;
+ }
+
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ if (adapter->int_count) {
+ adapter->int_count = 0;
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+ priv->hw_process_int_status(priv);
+ } else if (adapter->ps_state == PS_SLEEP &&
+ !skb_queue_empty(&adapter->tx_queue)) {
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+ adapter->wakeup_tries++;
+ priv->hw_wakeup_firmware(priv);
+ continue;
+ } else {
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+ }
+
+ if (adapter->ps_state == PS_SLEEP)
+ continue;
+
+ if (!priv->btmrvl_dev.tx_dnld_rdy)
+ continue;
+
+ skb = skb_dequeue(&adapter->tx_queue);
+ if (skb) {
+ if (btmrvl_tx_pkt(priv, skb))
+ priv->btmrvl_dev.hcidev->stat.err_tx++;
+ else
+ priv->btmrvl_dev.hcidev->stat.byte_tx += skb->len;
+
+ kfree_skb(skb);
+ }
+ }
+
+ return 0;
+}
+
+int btmrvl_register_hdev(struct btmrvl_private *priv)
+{
+ struct hci_dev *hdev = NULL;
+ int ret;
+
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BT_ERR("Can not allocate HCI device");
+ goto err_hdev;
+ }
+
+ priv->btmrvl_dev.hcidev = hdev;
+ hdev->driver_data = priv;
+
+ hdev->bus = HCI_SDIO;
+ hdev->open = btmrvl_open;
+ hdev->close = btmrvl_close;
+ hdev->flush = btmrvl_flush;
+ hdev->send = btmrvl_send_frame;
+ hdev->destruct = btmrvl_destruct;
+ hdev->ioctl = btmrvl_ioctl;
+ hdev->owner = THIS_MODULE;
+
+ btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
+
+ hdev->dev_type = priv->btmrvl_dev.dev_type;
+
+ ret = hci_register_dev(hdev);
+ if (ret < 0) {
+ BT_ERR("Can not register HCI device");
+ goto err_hci_register_dev;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ btmrvl_debugfs_init(hdev);
+#endif
+
+ return 0;
+
+err_hci_register_dev:
+ hci_free_dev(hdev);
+
+err_hdev:
+ /* Stop the thread servicing the interrupts */
+ kthread_stop(priv->main_thread.task);
+
+ btmrvl_free_adapter(priv);
+ kfree(priv);
+
+ return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(btmrvl_register_hdev);
+
+struct btmrvl_private *btmrvl_add_card(void *card)
+{
+ struct btmrvl_private *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ BT_ERR("Can not allocate priv");
+ goto err_priv;
+ }
+
+ priv->adapter = kzalloc(sizeof(*priv->adapter), GFP_KERNEL);
+ if (!priv->adapter) {
+ BT_ERR("Allocate buffer for btmrvl_adapter failed!");
+ goto err_adapter;
+ }
+
+ btmrvl_init_adapter(priv);
+
+ BT_DBG("Starting kthread...");
+ priv->main_thread.priv = priv;
+ spin_lock_init(&priv->driver_lock);
+
+ init_waitqueue_head(&priv->main_thread.wait_q);
+ priv->main_thread.task = kthread_run(btmrvl_service_main_thread,
+ &priv->main_thread, "btmrvl_main_service");
+
+ priv->btmrvl_dev.card = card;
+ priv->btmrvl_dev.tx_dnld_rdy = true;
+
+ return priv;
+
+err_adapter:
+ kfree(priv);
+
+err_priv:
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(btmrvl_add_card);
+
+int btmrvl_remove_card(struct btmrvl_private *priv)
+{
+ struct hci_dev *hdev;
+
+ hdev = priv->btmrvl_dev.hcidev;
+
+ wake_up_interruptible(&priv->adapter->cmd_wait_q);
+
+ kthread_stop(priv->main_thread.task);
+
+#ifdef CONFIG_DEBUG_FS
+ btmrvl_debugfs_remove(hdev);
+#endif
+
+ hci_unregister_dev(hdev);
+
+ hci_free_dev(hdev);
+
+ priv->btmrvl_dev.hcidev = NULL;
+
+ btmrvl_free_adapter(priv);
+
+ kfree(priv);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btmrvl_remove_card);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell Bluetooth driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c
new file mode 100644
index 0000000..9ef4816
--- /dev/null
+++ b/drivers/bluetooth/btmrvl_sdio.c
@@ -0,0 +1,1078 @@
+/**
+ * Marvell BT-over-SDIO driver: SDIO interface related functions.
+ *
+ * Copyright (C) 2009, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#include <linux/firmware.h>
+#include <linux/slab.h>
+
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/module.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btmrvl_drv.h"
+#include "btmrvl_sdio.h"
+
+#define VERSION "1.0"
+
+/* The btmrvl_sdio_remove() callback function is called
+ * when user removes this module from kernel space or ejects
+ * the card from the slot. The driver handles these 2 cases
+ * differently.
+ * If the user is removing the module, a MODULE_SHUTDOWN_REQ
+ * command is sent to firmware and interrupt will be disabled.
+ * If the card is removed, there is no need to send command
+ * or disable interrupt.
+ *
+ * The variable 'user_rmmod' is used to distinguish these two
+ * scenarios. This flag is initialized as FALSE in case the card
+ * is removed, and will be set to TRUE for module removal when
+ * module_exit function is called.
+ */
+static u8 user_rmmod;
+static u8 sdio_ireg;
+
+static const struct btmrvl_sdio_card_reg btmrvl_reg_8688 = {
+ .cfg = 0x03,
+ .host_int_mask = 0x04,
+ .host_intstatus = 0x05,
+ .card_status = 0x20,
+ .sq_read_base_addr_a0 = 0x10,
+ .sq_read_base_addr_a1 = 0x11,
+ .card_fw_status0 = 0x40,
+ .card_fw_status1 = 0x41,
+ .card_rx_len = 0x42,
+ .card_rx_unit = 0x43,
+ .io_port_0 = 0x00,
+ .io_port_1 = 0x01,
+ .io_port_2 = 0x02,
+};
+static const struct btmrvl_sdio_card_reg btmrvl_reg_8787 = {
+ .cfg = 0x00,
+ .host_int_mask = 0x02,
+ .host_intstatus = 0x03,
+ .card_status = 0x30,
+ .sq_read_base_addr_a0 = 0x40,
+ .sq_read_base_addr_a1 = 0x41,
+ .card_revision = 0x5c,
+ .card_fw_status0 = 0x60,
+ .card_fw_status1 = 0x61,
+ .card_rx_len = 0x62,
+ .card_rx_unit = 0x63,
+ .io_port_0 = 0x78,
+ .io_port_1 = 0x79,
+ .io_port_2 = 0x7a,
+};
+
+static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
+ .helper = "sd8688_helper.bin",
+ .firmware = "sd8688.bin",
+ .reg = &btmrvl_reg_8688,
+ .sd_blksz_fw_dl = 64,
+};
+
+static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
+ .helper = NULL,
+ .firmware = "mrvl/sd8787_uapsta.bin",
+ .reg = &btmrvl_reg_8787,
+ .sd_blksz_fw_dl = 256,
+};
+
+static const struct sdio_device_id btmrvl_sdio_ids[] = {
+ /* Marvell SD8688 Bluetooth device */
+ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105),
+ .driver_data = (unsigned long) &btmrvl_sdio_sd8688 },
+ /* Marvell SD8787 Bluetooth device */
+ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
+ .driver_data = (unsigned long) &btmrvl_sdio_sd8787 },
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(sdio, btmrvl_sdio_ids);
+
+static int btmrvl_sdio_get_rx_unit(struct btmrvl_sdio_card *card)
+{
+ u8 reg;
+ int ret;
+
+ reg = sdio_readb(card->func, card->reg->card_rx_unit, &ret);
+ if (!ret)
+ card->rx_unit = reg;
+
+ return ret;
+}
+
+static int btmrvl_sdio_read_fw_status(struct btmrvl_sdio_card *card, u16 *dat)
+{
+ u8 fws0, fws1;
+ int ret;
+
+ *dat = 0;
+
+ fws0 = sdio_readb(card->func, card->reg->card_fw_status0, &ret);
+ if (ret)
+ return -EIO;
+
+ fws1 = sdio_readb(card->func, card->reg->card_fw_status1, &ret);
+ if (ret)
+ return -EIO;
+
+ *dat = (((u16) fws1) << 8) | fws0;
+
+ return 0;
+}
+
+static int btmrvl_sdio_read_rx_len(struct btmrvl_sdio_card *card, u16 *dat)
+{
+ u8 reg;
+ int ret;
+
+ reg = sdio_readb(card->func, card->reg->card_rx_len, &ret);
+ if (!ret)
+ *dat = (u16) reg << card->rx_unit;
+
+ return ret;
+}
+
+static int btmrvl_sdio_enable_host_int_mask(struct btmrvl_sdio_card *card,
+ u8 mask)
+{
+ int ret;
+
+ sdio_writeb(card->func, mask, card->reg->host_int_mask, &ret);
+ if (ret) {
+ BT_ERR("Unable to enable the host interrupt!");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int btmrvl_sdio_disable_host_int_mask(struct btmrvl_sdio_card *card,
+ u8 mask)
+{
+ u8 host_int_mask;
+ int ret;
+
+ host_int_mask = sdio_readb(card->func, card->reg->host_int_mask, &ret);
+ if (ret)
+ return -EIO;
+
+ host_int_mask &= ~mask;
+
+ sdio_writeb(card->func, host_int_mask, card->reg->host_int_mask, &ret);
+ if (ret < 0) {
+ BT_ERR("Unable to disable the host interrupt!");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int btmrvl_sdio_poll_card_status(struct btmrvl_sdio_card *card, u8 bits)
+{
+ unsigned int tries;
+ u8 status;
+ int ret;
+
+ for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) {
+ status = sdio_readb(card->func, card->reg->card_status, &ret);
+ if (ret)
+ goto failed;
+ if ((status & bits) == bits)
+ return ret;
+
+ udelay(1);
+ }
+
+ ret = -ETIMEDOUT;
+
+failed:
+ BT_ERR("FAILED! ret=%d", ret);
+
+ return ret;
+}
+
+static int btmrvl_sdio_verify_fw_download(struct btmrvl_sdio_card *card,
+ int pollnum)
+{
+ int ret = -ETIMEDOUT;
+ u16 firmwarestat;
+ unsigned int tries;
+
+ /* Wait for firmware to become ready */
+ for (tries = 0; tries < pollnum; tries++) {
+ if (btmrvl_sdio_read_fw_status(card, &firmwarestat) < 0)
+ continue;
+
+ if (firmwarestat == FIRMWARE_READY) {
+ ret = 0;
+ break;
+ } else {
+ msleep(10);
+ }
+ }
+
+ return ret;
+}
+
+static int btmrvl_sdio_download_helper(struct btmrvl_sdio_card *card)
+{
+ const struct firmware *fw_helper = NULL;
+ const u8 *helper = NULL;
+ int ret;
+ void *tmphlprbuf = NULL;
+ int tmphlprbufsz, hlprblknow, helperlen;
+ u8 *helperbuf;
+ u32 tx_len;
+
+ ret = request_firmware(&fw_helper, card->helper,
+ &card->func->dev);
+ if ((ret < 0) || !fw_helper) {
+ BT_ERR("request_firmware(helper) failed, error code = %d",
+ ret);
+ ret = -ENOENT;
+ goto done;
+ }
+
+ helper = fw_helper->data;
+ helperlen = fw_helper->size;
+
+ BT_DBG("Downloading helper image (%d bytes), block size %d bytes",
+ helperlen, SDIO_BLOCK_SIZE);
+
+ tmphlprbufsz = ALIGN_SZ(BTM_UPLD_SIZE, BTSDIO_DMA_ALIGN);
+
+ tmphlprbuf = kzalloc(tmphlprbufsz, GFP_KERNEL);
+ if (!tmphlprbuf) {
+ BT_ERR("Unable to allocate buffer for helper."
+ " Terminating download");
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ helperbuf = (u8 *) ALIGN_ADDR(tmphlprbuf, BTSDIO_DMA_ALIGN);
+
+ /* Perform helper data transfer */
+ tx_len = (FIRMWARE_TRANSFER_NBLOCK * SDIO_BLOCK_SIZE)
+ - SDIO_HEADER_LEN;
+ hlprblknow = 0;
+
+ do {
+ ret = btmrvl_sdio_poll_card_status(card,
+ CARD_IO_READY | DN_LD_CARD_RDY);
+ if (ret < 0) {
+ BT_ERR("Helper download poll status timeout @ %d",
+ hlprblknow);
+ goto done;
+ }
+
+ /* Check if there is more data? */
+ if (hlprblknow >= helperlen)
+ break;
+
+ if (helperlen - hlprblknow < tx_len)
+ tx_len = helperlen - hlprblknow;
+
+ /* Little-endian */
+ helperbuf[0] = ((tx_len & 0x000000ff) >> 0);
+ helperbuf[1] = ((tx_len & 0x0000ff00) >> 8);
+ helperbuf[2] = ((tx_len & 0x00ff0000) >> 16);
+ helperbuf[3] = ((tx_len & 0xff000000) >> 24);
+
+ memcpy(&helperbuf[SDIO_HEADER_LEN], &helper[hlprblknow],
+ tx_len);
+
+ /* Now send the data */
+ ret = sdio_writesb(card->func, card->ioport, helperbuf,
+ FIRMWARE_TRANSFER_NBLOCK * SDIO_BLOCK_SIZE);
+ if (ret < 0) {
+ BT_ERR("IO error during helper download @ %d",
+ hlprblknow);
+ goto done;
+ }
+
+ hlprblknow += tx_len;
+ } while (true);
+
+ BT_DBG("Transferring helper image EOF block");
+
+ memset(helperbuf, 0x0, SDIO_BLOCK_SIZE);
+
+ ret = sdio_writesb(card->func, card->ioport, helperbuf,
+ SDIO_BLOCK_SIZE);
+ if (ret < 0) {
+ BT_ERR("IO error in writing helper image EOF block");
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ kfree(tmphlprbuf);
+ if (fw_helper)
+ release_firmware(fw_helper);
+
+ return ret;
+}
+
+static int btmrvl_sdio_download_fw_w_helper(struct btmrvl_sdio_card *card)
+{
+ const struct firmware *fw_firmware = NULL;
+ const u8 *firmware = NULL;
+ int firmwarelen, tmpfwbufsz, ret;
+ unsigned int tries, offset;
+ u8 base0, base1;
+ void *tmpfwbuf = NULL;
+ u8 *fwbuf;
+ u16 len, blksz_dl = card->sd_blksz_fw_dl;
+ int txlen = 0, tx_blocks = 0, count = 0;
+
+ ret = request_firmware(&fw_firmware, card->firmware,
+ &card->func->dev);
+ if ((ret < 0) || !fw_firmware) {
+ BT_ERR("request_firmware(firmware) failed, error code = %d",
+ ret);
+ ret = -ENOENT;
+ goto done;
+ }
+
+ firmware = fw_firmware->data;
+ firmwarelen = fw_firmware->size;
+
+ BT_DBG("Downloading FW image (%d bytes)", firmwarelen);
+
+ tmpfwbufsz = ALIGN_SZ(BTM_UPLD_SIZE, BTSDIO_DMA_ALIGN);
+ tmpfwbuf = kzalloc(tmpfwbufsz, GFP_KERNEL);
+ if (!tmpfwbuf) {
+ BT_ERR("Unable to allocate buffer for firmware."
+ " Terminating download");
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ /* Ensure aligned firmware buffer */
+ fwbuf = (u8 *) ALIGN_ADDR(tmpfwbuf, BTSDIO_DMA_ALIGN);
+
+ /* Perform firmware data transfer */
+ offset = 0;
+ do {
+ ret = btmrvl_sdio_poll_card_status(card,
+ CARD_IO_READY | DN_LD_CARD_RDY);
+ if (ret < 0) {
+ BT_ERR("FW download with helper poll status"
+ " timeout @ %d", offset);
+ goto done;
+ }
+
+ /* Check if there is more data ? */
+ if (offset >= firmwarelen)
+ break;
+
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+ base0 = sdio_readb(card->func,
+ card->reg->sq_read_base_addr_a0, &ret);
+ if (ret) {
+ BT_ERR("BASE0 register read failed:"
+ " base0 = 0x%04X(%d)."
+ " Terminating download",
+ base0, base0);
+ ret = -EIO;
+ goto done;
+ }
+ base1 = sdio_readb(card->func,
+ card->reg->sq_read_base_addr_a1, &ret);
+ if (ret) {
+ BT_ERR("BASE1 register read failed:"
+ " base1 = 0x%04X(%d)."
+ " Terminating download",
+ base1, base1);
+ ret = -EIO;
+ goto done;
+ }
+
+ len = (((u16) base1) << 8) | base0;
+ if (len)
+ break;
+
+ udelay(10);
+ }
+
+ if (!len)
+ break;
+ else if (len > BTM_UPLD_SIZE) {
+ BT_ERR("FW download failure @%d, invalid length %d",
+ offset, len);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ txlen = len;
+
+ if (len & BIT(0)) {
+ count++;
+ if (count > MAX_WRITE_IOMEM_RETRY) {
+ BT_ERR("FW download failure @%d, "
+ "over max retry count", offset);
+ ret = -EIO;
+ goto done;
+ }
+ BT_ERR("FW CRC error indicated by the helper: "
+ "len = 0x%04X, txlen = %d", len, txlen);
+ len &= ~BIT(0);
+ /* Set txlen to 0 so as to resend from same offset */
+ txlen = 0;
+ } else {
+ count = 0;
+
+ /* Last block ? */
+ if (firmwarelen - offset < txlen)
+ txlen = firmwarelen - offset;
+
+ tx_blocks = (txlen + blksz_dl - 1) / blksz_dl;
+
+ memcpy(fwbuf, &firmware[offset], txlen);
+ }
+
+ ret = sdio_writesb(card->func, card->ioport, fwbuf,
+ tx_blocks * blksz_dl);
+
+ if (ret < 0) {
+ BT_ERR("FW download, writesb(%d) failed @%d",
+ count, offset);
+ sdio_writeb(card->func, HOST_CMD53_FIN,
+ card->reg->cfg, &ret);
+ if (ret)
+ BT_ERR("writeb failed (CFG)");
+ }
+
+ offset += txlen;
+ } while (true);
+
+ BT_DBG("FW download over, size %d bytes", offset);
+
+ ret = 0;
+
+done:
+ kfree(tmpfwbuf);
+
+ if (fw_firmware)
+ release_firmware(fw_firmware);
+
+ return ret;
+}
+
+static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
+{
+ u16 buf_len = 0;
+ int ret, buf_block_len, blksz;
+ struct sk_buff *skb = NULL;
+ u32 type;
+ u8 *payload = NULL;
+ struct hci_dev *hdev = priv->btmrvl_dev.hcidev;
+ struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+
+ if (!card || !card->func) {
+ BT_ERR("card or function is NULL!");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* Read the length of data to be transferred */
+ ret = btmrvl_sdio_read_rx_len(card, &buf_len);
+ if (ret < 0) {
+ BT_ERR("read rx_len failed");
+ ret = -EIO;
+ goto exit;
+ }
+
+ blksz = SDIO_BLOCK_SIZE;
+ buf_block_len = (buf_len + blksz - 1) / blksz;
+
+ if (buf_len <= SDIO_HEADER_LEN
+ || (buf_block_len * blksz) > ALLOC_BUF_SIZE) {
+ BT_ERR("invalid packet length: %d", buf_len);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* Allocate buffer */
+ skb = bt_skb_alloc(buf_block_len * blksz + BTSDIO_DMA_ALIGN,
+ GFP_ATOMIC);
+ if (skb == NULL) {
+ BT_ERR("No free skb");
+ goto exit;
+ }
+
+ if ((unsigned long) skb->data & (BTSDIO_DMA_ALIGN - 1)) {
+ skb_put(skb, (unsigned long) skb->data &
+ (BTSDIO_DMA_ALIGN - 1));
+ skb_pull(skb, (unsigned long) skb->data &
+ (BTSDIO_DMA_ALIGN - 1));
+ }
+
+ payload = skb->data;
+
+ ret = sdio_readsb(card->func, payload, card->ioport,
+ buf_block_len * blksz);
+ if (ret < 0) {
+ BT_ERR("readsb failed: %d", ret);
+ ret = -EIO;
+ goto exit;
+ }
+
+ /* This is SDIO specific header length: byte[2][1][0], type: byte[3]
+ * (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor)
+ */
+
+ buf_len = payload[0];
+ buf_len |= (u16) payload[1] << 8;
+ type = payload[3];
+
+ switch (type) {
+ case HCI_ACLDATA_PKT:
+ case HCI_SCODATA_PKT:
+ case HCI_EVENT_PKT:
+ bt_cb(skb)->pkt_type = type;
+ skb->dev = (void *)hdev;
+ skb_put(skb, buf_len);
+ skb_pull(skb, SDIO_HEADER_LEN);
+
+ if (type == HCI_EVENT_PKT)
+ btmrvl_check_evtpkt(priv, skb);
+
+ hci_recv_frame(skb);
+ hdev->stat.byte_rx += buf_len;
+ break;
+
+ case MRVL_VENDOR_PKT:
+ bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
+ skb->dev = (void *)hdev;
+ skb_put(skb, buf_len);
+ skb_pull(skb, SDIO_HEADER_LEN);
+
+ if (btmrvl_process_event(priv, skb))
+ hci_recv_frame(skb);
+
+ hdev->stat.byte_rx += buf_len;
+ break;
+
+ default:
+ BT_ERR("Unknown packet type:%d", type);
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, payload,
+ blksz * buf_block_len);
+
+ kfree_skb(skb);
+ skb = NULL;
+ break;
+ }
+
+exit:
+ if (ret) {
+ hdev->stat.err_rx++;
+ if (skb)
+ kfree_skb(skb);
+ }
+
+ return ret;
+}
+
+static int btmrvl_sdio_process_int_status(struct btmrvl_private *priv)
+{
+ ulong flags;
+ u8 ireg;
+ struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ ireg = sdio_ireg;
+ sdio_ireg = 0;
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+ sdio_claim_host(card->func);
+ if (ireg & DN_LD_HOST_INT_STATUS) {
+ if (priv->btmrvl_dev.tx_dnld_rdy)
+ BT_DBG("tx_done already received: "
+ " int_status=0x%x", ireg);
+ else
+ priv->btmrvl_dev.tx_dnld_rdy = true;
+ }
+
+ if (ireg & UP_LD_HOST_INT_STATUS)
+ btmrvl_sdio_card_to_host(priv);
+
+ sdio_release_host(card->func);
+
+ return 0;
+}
+
+static void btmrvl_sdio_interrupt(struct sdio_func *func)
+{
+ struct btmrvl_private *priv;
+ struct btmrvl_sdio_card *card;
+ ulong flags;
+ u8 ireg = 0;
+ int ret;
+
+ card = sdio_get_drvdata(func);
+ if (!card || !card->priv) {
+ BT_ERR("sbi_interrupt(%p) card or priv is "
+ "NULL, card=%p\n", func, card);
+ return;
+ }
+
+ priv = card->priv;
+
+ ireg = sdio_readb(card->func, card->reg->host_intstatus, &ret);
+ if (ret) {
+ BT_ERR("sdio_readb: read int status register failed");
+ return;
+ }
+
+ if (ireg != 0) {
+ /*
+ * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+ * Clear the interrupt status register and re-enable the
+ * interrupt.
+ */
+ BT_DBG("ireg = 0x%x", ireg);
+
+ sdio_writeb(card->func, ~(ireg) & (DN_LD_HOST_INT_STATUS |
+ UP_LD_HOST_INT_STATUS),
+ card->reg->host_intstatus, &ret);
+ if (ret) {
+ BT_ERR("sdio_writeb: clear int status register failed");
+ return;
+ }
+ }
+
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ sdio_ireg |= ireg;
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+ btmrvl_interrupt(priv);
+}
+
+static int btmrvl_sdio_register_dev(struct btmrvl_sdio_card *card)
+{
+ struct sdio_func *func;
+ u8 reg;
+ int ret = 0;
+
+ if (!card || !card->func) {
+ BT_ERR("Error: card or function is NULL!");
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ func = card->func;
+
+ sdio_claim_host(func);
+
+ ret = sdio_enable_func(func);
+ if (ret) {
+ BT_ERR("sdio_enable_func() failed: ret=%d", ret);
+ ret = -EIO;
+ goto release_host;
+ }
+
+ ret = sdio_claim_irq(func, btmrvl_sdio_interrupt);
+ if (ret) {
+ BT_ERR("sdio_claim_irq failed: ret=%d", ret);
+ ret = -EIO;
+ goto disable_func;
+ }
+
+ ret = sdio_set_block_size(card->func, SDIO_BLOCK_SIZE);
+ if (ret) {
+ BT_ERR("cannot set SDIO block size");
+ ret = -EIO;
+ goto release_irq;
+ }
+
+ reg = sdio_readb(func, card->reg->io_port_0, &ret);
+ if (ret < 0) {
+ ret = -EIO;
+ goto release_irq;
+ }
+
+ card->ioport = reg;
+
+ reg = sdio_readb(func, card->reg->io_port_1, &ret);
+ if (ret < 0) {
+ ret = -EIO;
+ goto release_irq;
+ }
+
+ card->ioport |= (reg << 8);
+
+ reg = sdio_readb(func, card->reg->io_port_2, &ret);
+ if (ret < 0) {
+ ret = -EIO;
+ goto release_irq;
+ }
+
+ card->ioport |= (reg << 16);
+
+ BT_DBG("SDIO FUNC%d IO port: 0x%x", func->num, card->ioport);
+
+ sdio_set_drvdata(func, card);
+
+ sdio_release_host(func);
+
+ return 0;
+
+release_irq:
+ sdio_release_irq(func);
+
+disable_func:
+ sdio_disable_func(func);
+
+release_host:
+ sdio_release_host(func);
+
+failed:
+ return ret;
+}
+
+static int btmrvl_sdio_unregister_dev(struct btmrvl_sdio_card *card)
+{
+ if (card && card->func) {
+ sdio_claim_host(card->func);
+ sdio_release_irq(card->func);
+ sdio_disable_func(card->func);
+ sdio_release_host(card->func);
+ sdio_set_drvdata(card->func, NULL);
+ }
+
+ return 0;
+}
+
+static int btmrvl_sdio_enable_host_int(struct btmrvl_sdio_card *card)
+{
+ int ret;
+
+ if (!card || !card->func)
+ return -EINVAL;
+
+ sdio_claim_host(card->func);
+
+ ret = btmrvl_sdio_enable_host_int_mask(card, HIM_ENABLE);
+
+ btmrvl_sdio_get_rx_unit(card);
+
+ sdio_release_host(card->func);
+
+ return ret;
+}
+
+static int btmrvl_sdio_disable_host_int(struct btmrvl_sdio_card *card)
+{
+ int ret;
+
+ if (!card || !card->func)
+ return -EINVAL;
+
+ sdio_claim_host(card->func);
+
+ ret = btmrvl_sdio_disable_host_int_mask(card, HIM_DISABLE);
+
+ sdio_release_host(card->func);
+
+ return ret;
+}
+
+static int btmrvl_sdio_host_to_card(struct btmrvl_private *priv,
+ u8 *payload, u16 nb)
+{
+ struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+ int ret = 0;
+ int buf_block_len;
+ int blksz;
+ int i = 0;
+ u8 *buf = NULL;
+ void *tmpbuf = NULL;
+ int tmpbufsz;
+
+ if (!card || !card->func) {
+ BT_ERR("card or function is NULL!");
+ return -EINVAL;
+ }
+
+ buf = payload;
+ if ((unsigned long) payload & (BTSDIO_DMA_ALIGN - 1)) {
+ tmpbufsz = ALIGN_SZ(nb, BTSDIO_DMA_ALIGN);
+ tmpbuf = kzalloc(tmpbufsz, GFP_KERNEL);
+ if (!tmpbuf)
+ return -ENOMEM;
+ buf = (u8 *) ALIGN_ADDR(tmpbuf, BTSDIO_DMA_ALIGN);
+ memcpy(buf, payload, nb);
+ }
+
+ blksz = SDIO_BLOCK_SIZE;
+ buf_block_len = (nb + blksz - 1) / blksz;
+
+ sdio_claim_host(card->func);
+
+ do {
+ /* Transfer data to card */
+ ret = sdio_writesb(card->func, card->ioport, buf,
+ buf_block_len * blksz);
+ if (ret < 0) {
+ i++;
+ BT_ERR("i=%d writesb failed: %d", i, ret);
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+ payload, nb);
+ ret = -EIO;
+ if (i > MAX_WRITE_IOMEM_RETRY)
+ goto exit;
+ }
+ } while (ret);
+
+ priv->btmrvl_dev.tx_dnld_rdy = false;
+
+exit:
+ sdio_release_host(card->func);
+ kfree(tmpbuf);
+
+ return ret;
+}
+
+static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card)
+{
+ int ret = 0;
+ u8 fws0;
+ int pollnum = MAX_POLL_TRIES;
+
+ if (!card || !card->func) {
+ BT_ERR("card or function is NULL!");
+ return -EINVAL;
+ }
+ sdio_claim_host(card->func);
+
+ if (!btmrvl_sdio_verify_fw_download(card, 1)) {
+ BT_DBG("Firmware already downloaded!");
+ goto done;
+ }
+
+ /* Check if other function driver is downloading the firmware */
+ fws0 = sdio_readb(card->func, card->reg->card_fw_status0, &ret);
+ if (ret) {
+ BT_ERR("Failed to read FW downloading status!");
+ ret = -EIO;
+ goto done;
+ }
+ if (fws0) {
+ BT_DBG("BT not the winner (%#x). Skip FW downloading", fws0);
+
+ /* Give other function more time to download the firmware */
+ pollnum *= 10;
+ } else {
+ if (card->helper) {
+ ret = btmrvl_sdio_download_helper(card);
+ if (ret) {
+ BT_ERR("Failed to download helper!");
+ ret = -EIO;
+ goto done;
+ }
+ }
+
+ if (btmrvl_sdio_download_fw_w_helper(card)) {
+ BT_ERR("Failed to download firmware!");
+ ret = -EIO;
+ goto done;
+ }
+ }
+
+ if (btmrvl_sdio_verify_fw_download(card, pollnum)) {
+ BT_ERR("FW failed to be active in time!");
+ ret = -ETIMEDOUT;
+ goto done;
+ }
+
+done:
+ sdio_release_host(card->func);
+
+ return ret;
+}
+
+static int btmrvl_sdio_wakeup_fw(struct btmrvl_private *priv)
+{
+ struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+ int ret = 0;
+
+ if (!card || !card->func) {
+ BT_ERR("card or function is NULL!");
+ return -EINVAL;
+ }
+
+ sdio_claim_host(card->func);
+
+ sdio_writeb(card->func, HOST_POWER_UP, card->reg->cfg, &ret);
+
+ sdio_release_host(card->func);
+
+ BT_DBG("wake up firmware");
+
+ return ret;
+}
+
+static int btmrvl_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ int ret = 0;
+ struct btmrvl_private *priv = NULL;
+ struct btmrvl_sdio_card *card = NULL;
+
+ BT_INFO("vendor=0x%x, device=0x%x, class=%d, fn=%d",
+ id->vendor, id->device, id->class, func->num);
+
+ card = kzalloc(sizeof(*card), GFP_KERNEL);
+ if (!card) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ card->func = func;
+
+ if (id->driver_data) {
+ struct btmrvl_sdio_device *data = (void *) id->driver_data;
+ card->helper = data->helper;
+ card->firmware = data->firmware;
+ card->reg = data->reg;
+ card->sd_blksz_fw_dl = data->sd_blksz_fw_dl;
+ }
+
+ if (btmrvl_sdio_register_dev(card) < 0) {
+ BT_ERR("Failed to register BT device!");
+ ret = -ENODEV;
+ goto free_card;
+ }
+
+ /* Disable the interrupts on the card */
+ btmrvl_sdio_disable_host_int(card);
+
+ if (btmrvl_sdio_download_fw(card)) {
+ BT_ERR("Downloading firmware failed!");
+ ret = -ENODEV;
+ goto unreg_dev;
+ }
+
+ msleep(100);
+
+ btmrvl_sdio_enable_host_int(card);
+
+ priv = btmrvl_add_card(card);
+ if (!priv) {
+ BT_ERR("Initializing card failed!");
+ ret = -ENODEV;
+ goto disable_host_int;
+ }
+
+ card->priv = priv;
+
+ /* Initialize the interface specific function pointers */
+ priv->hw_host_to_card = btmrvl_sdio_host_to_card;
+ priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw;
+ priv->hw_process_int_status = btmrvl_sdio_process_int_status;
+
+ if (btmrvl_register_hdev(priv)) {
+ BT_ERR("Register hdev failed!");
+ ret = -ENODEV;
+ goto disable_host_int;
+ }
+
+ priv->btmrvl_dev.psmode = 1;
+ btmrvl_enable_ps(priv);
+
+ return 0;
+
+disable_host_int:
+ btmrvl_sdio_disable_host_int(card);
+unreg_dev:
+ btmrvl_sdio_unregister_dev(card);
+free_card:
+ kfree(card);
+done:
+ return ret;
+}
+
+static void btmrvl_sdio_remove(struct sdio_func *func)
+{
+ struct btmrvl_sdio_card *card;
+
+ if (func) {
+ card = sdio_get_drvdata(func);
+ if (card) {
+ /* Send SHUTDOWN command & disable interrupt
+ * if user removes the module.
+ */
+ if (user_rmmod) {
+ btmrvl_send_module_cfg_cmd(card->priv,
+ MODULE_SHUTDOWN_REQ);
+ btmrvl_sdio_disable_host_int(card);
+ }
+ BT_DBG("unregester dev");
+ btmrvl_sdio_unregister_dev(card);
+ btmrvl_remove_card(card->priv);
+ kfree(card);
+ }
+ }
+}
+
+static struct sdio_driver bt_mrvl_sdio = {
+ .name = "btmrvl_sdio",
+ .id_table = btmrvl_sdio_ids,
+ .probe = btmrvl_sdio_probe,
+ .remove = btmrvl_sdio_remove,
+};
+
+static int __init btmrvl_sdio_init_module(void)
+{
+ if (sdio_register_driver(&bt_mrvl_sdio) != 0) {
+ BT_ERR("SDIO Driver Registration Failed");
+ return -ENODEV;
+ }
+
+ /* Clear the flag in case user removes the card. */
+ user_rmmod = 0;
+
+ return 0;
+}
+
+static void __exit btmrvl_sdio_exit_module(void)
+{
+ /* Set the flag as user is removing this module. */
+ user_rmmod = 1;
+
+ sdio_unregister_driver(&bt_mrvl_sdio);
+}
+
+module_init(btmrvl_sdio_init_module);
+module_exit(btmrvl_sdio_exit_module);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell BT-over-SDIO driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_FIRMWARE("sd8688_helper.bin");
+MODULE_FIRMWARE("sd8688.bin");
+MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
diff --git a/drivers/bluetooth/btmrvl_sdio.h b/drivers/bluetooth/btmrvl_sdio.h
new file mode 100644
index 0000000..43d35a6
--- /dev/null
+++ b/drivers/bluetooth/btmrvl_sdio.h
@@ -0,0 +1,112 @@
+/**
+ * Marvell BT-over-SDIO driver: SDIO interface related definitions
+ *
+ * Copyright (C) 2009, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ *
+ **/
+
+#define SDIO_HEADER_LEN 4
+
+/* SD block size can not bigger than 64 due to buf size limit in firmware */
+/* define SD block size for data Tx/Rx */
+#define SDIO_BLOCK_SIZE 64
+
+/* Number of blocks for firmware transfer */
+#define FIRMWARE_TRANSFER_NBLOCK 2
+
+/* This is for firmware specific length */
+#define FW_EXTRA_LEN 36
+
+#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024)
+
+#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \
+ (HCI_MAX_FRAME_SIZE + FW_EXTRA_LEN)
+
+#define ALLOC_BUF_SIZE (((max_t (int, MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \
+ MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \
+ + SDIO_BLOCK_SIZE - 1) / SDIO_BLOCK_SIZE) \
+ * SDIO_BLOCK_SIZE)
+
+/* The number of times to try when polling for status */
+#define MAX_POLL_TRIES 100
+
+/* Max retry number of CMD53 write */
+#define MAX_WRITE_IOMEM_RETRY 2
+
+/* register bitmasks */
+#define HOST_POWER_UP BIT(1)
+#define HOST_CMD53_FIN BIT(2)
+
+#define HIM_DISABLE 0xff
+#define HIM_ENABLE (BIT(0) | BIT(1))
+
+#define UP_LD_HOST_INT_STATUS BIT(0)
+#define DN_LD_HOST_INT_STATUS BIT(1)
+
+#define DN_LD_CARD_RDY BIT(0)
+#define CARD_IO_READY BIT(3)
+
+#define FIRMWARE_READY 0xfedc
+
+
+struct btmrvl_sdio_card_reg {
+ u8 cfg;
+ u8 host_int_mask;
+ u8 host_intstatus;
+ u8 card_status;
+ u8 sq_read_base_addr_a0;
+ u8 sq_read_base_addr_a1;
+ u8 card_revision;
+ u8 card_fw_status0;
+ u8 card_fw_status1;
+ u8 card_rx_len;
+ u8 card_rx_unit;
+ u8 io_port_0;
+ u8 io_port_1;
+ u8 io_port_2;
+};
+
+struct btmrvl_sdio_card {
+ struct sdio_func *func;
+ u32 ioport;
+ const char *helper;
+ const char *firmware;
+ const struct btmrvl_sdio_card_reg *reg;
+ u16 sd_blksz_fw_dl;
+ u8 rx_unit;
+ struct btmrvl_private *priv;
+};
+
+struct btmrvl_sdio_device {
+ const char *helper;
+ const char *firmware;
+ const struct btmrvl_sdio_card_reg *reg;
+ u16 sd_blksz_fw_dl;
+};
+
+
+/* Platform specific DMA alignment */
+#define BTSDIO_DMA_ALIGN 8
+
+/* Macros for Data Alignment : size */
+#define ALIGN_SZ(p, a) \
+ (((p) + ((a) - 1)) & ~((a) - 1))
+
+/* Macros for Data Alignment : address */
+#define ALIGN_ADDR(p, a) \
+ ((((unsigned long)(p)) + (((unsigned long)(a)) - 1)) & \
+ ~(((unsigned long)(a)) - 1))
diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c
new file mode 100644
index 0000000..792e32d
--- /dev/null
+++ b/drivers/bluetooth/btsdio.c
@@ -0,0 +1,408 @@
+/*
+ *
+ * Generic Bluetooth SDIO driver
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ * Copyright (C) 2007 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#define VERSION "0.1"
+
+static const struct sdio_device_id btsdio_table[] = {
+ /* Generic Bluetooth Type-A SDIO device */
+ { SDIO_DEVICE_CLASS(SDIO_CLASS_BT_A) },
+
+ /* Generic Bluetooth Type-B SDIO device */
+ { SDIO_DEVICE_CLASS(SDIO_CLASS_BT_B) },
+
+ /* Generic Bluetooth AMP controller */
+ { SDIO_DEVICE_CLASS(SDIO_CLASS_BT_AMP) },
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(sdio, btsdio_table);
+
+struct btsdio_data {
+ struct hci_dev *hdev;
+ struct sdio_func *func;
+
+ struct work_struct work;
+
+ struct sk_buff_head txq;
+};
+
+#define REG_RDAT 0x00 /* Receiver Data */
+#define REG_TDAT 0x00 /* Transmitter Data */
+#define REG_PC_RRT 0x10 /* Read Packet Control */
+#define REG_PC_WRT 0x11 /* Write Packet Control */
+#define REG_RTC_STAT 0x12 /* Retry Control Status */
+#define REG_RTC_SET 0x12 /* Retry Control Set */
+#define REG_INTRD 0x13 /* Interrupt Indication */
+#define REG_CL_INTRD 0x13 /* Interrupt Clear */
+#define REG_EN_INTRD 0x14 /* Interrupt Enable */
+#define REG_MD_STAT 0x20 /* Bluetooth Mode Status */
+
+static int btsdio_tx_packet(struct btsdio_data *data, struct sk_buff *skb)
+{
+ int err;
+
+ BT_DBG("%s", data->hdev->name);
+
+ /* Prepend Type-A header */
+ skb_push(skb, 4);
+ skb->data[0] = (skb->len & 0x0000ff);
+ skb->data[1] = (skb->len & 0x00ff00) >> 8;
+ skb->data[2] = (skb->len & 0xff0000) >> 16;
+ skb->data[3] = bt_cb(skb)->pkt_type;
+
+ err = sdio_writesb(data->func, REG_TDAT, skb->data, skb->len);
+ if (err < 0) {
+ skb_pull(skb, 4);
+ sdio_writeb(data->func, 0x01, REG_PC_WRT, NULL);
+ return err;
+ }
+
+ data->hdev->stat.byte_tx += skb->len;
+
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static void btsdio_work(struct work_struct *work)
+{
+ struct btsdio_data *data = container_of(work, struct btsdio_data, work);
+ struct sk_buff *skb;
+ int err;
+
+ BT_DBG("%s", data->hdev->name);
+
+ sdio_claim_host(data->func);
+
+ while ((skb = skb_dequeue(&data->txq))) {
+ err = btsdio_tx_packet(data, skb);
+ if (err < 0) {
+ data->hdev->stat.err_tx++;
+ skb_queue_head(&data->txq, skb);
+ break;
+ }
+ }
+
+ sdio_release_host(data->func);
+}
+
+static int btsdio_rx_packet(struct btsdio_data *data)
+{
+ u8 hdr[4] __attribute__ ((aligned(4)));
+ struct sk_buff *skb;
+ int err, len;
+
+ BT_DBG("%s", data->hdev->name);
+
+ err = sdio_readsb(data->func, hdr, REG_RDAT, 4);
+ if (err < 0)
+ return err;
+
+ len = hdr[0] | (hdr[1] << 8) | (hdr[2] << 16);
+ if (len < 4 || len > 65543)
+ return -EILSEQ;
+
+ skb = bt_skb_alloc(len - 4, GFP_KERNEL);
+ if (!skb) {
+ /* Out of memory. Prepare a read retry and just
+ * return with the expectation that the next time
+ * we're called we'll have more memory. */
+ return -ENOMEM;
+ }
+
+ skb_put(skb, len - 4);
+
+ err = sdio_readsb(data->func, skb->data, REG_RDAT, len - 4);
+ if (err < 0) {
+ kfree_skb(skb);
+ return err;
+ }
+
+ data->hdev->stat.byte_rx += len;
+
+ skb->dev = (void *) data->hdev;
+ bt_cb(skb)->pkt_type = hdr[3];
+
+ err = hci_recv_frame(skb);
+ if (err < 0)
+ return err;
+
+ sdio_writeb(data->func, 0x00, REG_PC_RRT, NULL);
+
+ return 0;
+}
+
+static void btsdio_interrupt(struct sdio_func *func)
+{
+ struct btsdio_data *data = sdio_get_drvdata(func);
+ int intrd;
+
+ BT_DBG("%s", data->hdev->name);
+
+ intrd = sdio_readb(func, REG_INTRD, NULL);
+ if (intrd & 0x01) {
+ sdio_writeb(func, 0x01, REG_CL_INTRD, NULL);
+
+ if (btsdio_rx_packet(data) < 0) {
+ data->hdev->stat.err_rx++;
+ sdio_writeb(data->func, 0x01, REG_PC_RRT, NULL);
+ }
+ }
+}
+
+static int btsdio_open(struct hci_dev *hdev)
+{
+ struct btsdio_data *data = hdev->driver_data;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ sdio_claim_host(data->func);
+
+ err = sdio_enable_func(data->func);
+ if (err < 0) {
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ goto release;
+ }
+
+ err = sdio_claim_irq(data->func, btsdio_interrupt);
+ if (err < 0) {
+ sdio_disable_func(data->func);
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ goto release;
+ }
+
+ if (data->func->class == SDIO_CLASS_BT_B)
+ sdio_writeb(data->func, 0x00, REG_MD_STAT, NULL);
+
+ sdio_writeb(data->func, 0x01, REG_EN_INTRD, NULL);
+
+release:
+ sdio_release_host(data->func);
+
+ return err;
+}
+
+static int btsdio_close(struct hci_dev *hdev)
+{
+ struct btsdio_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ sdio_claim_host(data->func);
+
+ sdio_writeb(data->func, 0x00, REG_EN_INTRD, NULL);
+
+ sdio_release_irq(data->func);
+ sdio_disable_func(data->func);
+
+ sdio_release_host(data->func);
+
+ return 0;
+}
+
+static int btsdio_flush(struct hci_dev *hdev)
+{
+ struct btsdio_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ skb_queue_purge(&data->txq);
+
+ return 0;
+}
+
+static int btsdio_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct btsdio_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+
+ default:
+ return -EILSEQ;
+ }
+
+ skb_queue_tail(&data->txq, skb);
+
+ schedule_work(&data->work);
+
+ return 0;
+}
+
+static void btsdio_destruct(struct hci_dev *hdev)
+{
+ struct btsdio_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ kfree(data);
+}
+
+static int btsdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct btsdio_data *data;
+ struct hci_dev *hdev;
+ struct sdio_func_tuple *tuple = func->tuples;
+ int err;
+
+ BT_DBG("func %p id %p class 0x%04x", func, id, func->class);
+
+ while (tuple) {
+ BT_DBG("code 0x%x size %d", tuple->code, tuple->size);
+ tuple = tuple->next;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->func = func;
+
+ INIT_WORK(&data->work, btsdio_work);
+
+ skb_queue_head_init(&data->txq);
+
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ hdev->bus = HCI_SDIO;
+ hdev->driver_data = data;
+
+ if (id->class == SDIO_CLASS_BT_AMP)
+ hdev->dev_type = HCI_AMP;
+ else
+ hdev->dev_type = HCI_BREDR;
+
+ data->hdev = hdev;
+
+ SET_HCIDEV_DEV(hdev, &func->dev);
+
+ hdev->open = btsdio_open;
+ hdev->close = btsdio_close;
+ hdev->flush = btsdio_flush;
+ hdev->send = btsdio_send_frame;
+ hdev->destruct = btsdio_destruct;
+
+ hdev->owner = THIS_MODULE;
+
+ err = hci_register_dev(hdev);
+ if (err < 0) {
+ hci_free_dev(hdev);
+ kfree(data);
+ return err;
+ }
+
+ sdio_set_drvdata(func, data);
+
+ return 0;
+}
+
+static void btsdio_remove(struct sdio_func *func)
+{
+ struct btsdio_data *data = sdio_get_drvdata(func);
+ struct hci_dev *hdev;
+
+ BT_DBG("func %p", func);
+
+ if (!data)
+ return;
+
+ hdev = data->hdev;
+
+ sdio_set_drvdata(func, NULL);
+
+ hci_unregister_dev(hdev);
+
+ hci_free_dev(hdev);
+}
+
+static struct sdio_driver btsdio_driver = {
+ .name = "btsdio",
+ .probe = btsdio_probe,
+ .remove = btsdio_remove,
+ .id_table = btsdio_table,
+};
+
+static int __init btsdio_init(void)
+{
+ BT_INFO("Generic Bluetooth SDIO driver ver %s", VERSION);
+
+ return sdio_register_driver(&btsdio_driver);
+}
+
+static void __exit btsdio_exit(void)
+{
+ sdio_unregister_driver(&btsdio_driver);
+}
+
+module_init(btsdio_init);
+module_exit(btsdio_exit);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Generic Bluetooth SDIO driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c
new file mode 100644
index 0000000..00c811f
--- /dev/null
+++ b/drivers/bluetooth/btuart_cs.c
@@ -0,0 +1,829 @@
+/*
+ *
+ * Driver for Bluetooth PCMCIA cards with HCI UART interface
+ *
+ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/bitops.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth driver for Bluetooth PCMCIA cards with HCI UART interface");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct btuart_info_t {
+ struct pcmcia_device *p_dev;
+
+ struct hci_dev *hdev;
+
+ spinlock_t lock; /* For serializing operations */
+
+ struct sk_buff_head txq;
+ unsigned long tx_state;
+
+ unsigned long rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+} btuart_info_t;
+
+
+static int btuart_config(struct pcmcia_device *link);
+static void btuart_release(struct pcmcia_device *link);
+
+static void btuart_detach(struct pcmcia_device *p_dev);
+
+
+/* Maximum baud rate */
+#define SPEED_MAX 115200
+
+/* Default baud rate: 57600, 115200, 230400 or 460800 */
+#define DEFAULT_BAUD_RATE 115200
+
+
+/* Transmit states */
+#define XMIT_SENDING 1
+#define XMIT_WAKEUP 2
+#define XMIT_WAITING 8
+
+/* Receiver states */
+#define RECV_WAIT_PACKET_TYPE 0
+#define RECV_WAIT_EVENT_HEADER 1
+#define RECV_WAIT_ACL_HEADER 2
+#define RECV_WAIT_SCO_HEADER 3
+#define RECV_WAIT_DATA 4
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int btuart_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
+{
+ int actual = 0;
+
+ /* Tx FIFO should be empty */
+ if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
+ return 0;
+
+ /* Fill FIFO with current frame */
+ while ((fifo_size-- > 0) && (actual < len)) {
+ /* Transmit next byte */
+ outb(buf[actual], iobase + UART_TX);
+ actual++;
+ }
+
+ return actual;
+}
+
+
+static void btuart_write_wakeup(btuart_info_t *info)
+{
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
+ set_bit(XMIT_WAKEUP, &(info->tx_state));
+ return;
+ }
+
+ do {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ register unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+ register unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+ register struct sk_buff *skb;
+ register int len;
+
+ clear_bit(XMIT_WAKEUP, &(info->tx_state));
+
+ if (!pcmcia_dev_present(info->p_dev))
+ return;
+
+ if (!(skb = skb_dequeue(&(info->txq))))
+ break;
+
+ /* Send frame */
+ len = btuart_write(iobase, 16, skb->data, skb->len);
+ set_bit(XMIT_WAKEUP, &(info->tx_state));
+
+ if (len == skb->len) {
+ kfree_skb(skb);
+ } else {
+ skb_pull(skb, len);
+ skb_queue_head(&(info->txq), skb);
+ }
+
+ info->hdev->stat.byte_tx += len;
+
+ } while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
+
+ clear_bit(XMIT_SENDING, &(info->tx_state));
+}
+
+
+static void btuart_receive(btuart_info_t *info)
+{
+ unsigned int iobase;
+ int boguscount = 0;
+
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ iobase = info->p_dev->resource[0]->start;
+#else
+ iobase = info->p_dev->io.BasePort1;
+#endif
+
+ do {
+ info->hdev->stat.byte_rx++;
+
+ /* Allocate packet */
+ if (info->rx_skb == NULL) {
+ info->rx_state = RECV_WAIT_PACKET_TYPE;
+ info->rx_count = 0;
+ if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate mem for new packet");
+ return;
+ }
+ }
+
+ if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
+
+ info->rx_skb->dev = (void *) info->hdev;
+ bt_cb(info->rx_skb)->pkt_type = inb(iobase + UART_RX);
+
+ switch (bt_cb(info->rx_skb)->pkt_type) {
+
+ case HCI_EVENT_PKT:
+ info->rx_state = RECV_WAIT_EVENT_HEADER;
+ info->rx_count = HCI_EVENT_HDR_SIZE;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ info->rx_state = RECV_WAIT_ACL_HEADER;
+ info->rx_count = HCI_ACL_HDR_SIZE;
+ break;
+
+ case HCI_SCODATA_PKT:
+ info->rx_state = RECV_WAIT_SCO_HEADER;
+ info->rx_count = HCI_SCO_HDR_SIZE;
+ break;
+
+ default:
+ /* Unknown packet */
+ BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+ info->hdev->stat.err_rx++;
+ clear_bit(HCI_RUNNING, &(info->hdev->flags));
+
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+
+ }
+
+ } else {
+
+ *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
+ info->rx_count--;
+
+ if (info->rx_count == 0) {
+
+ int dlen;
+ struct hci_event_hdr *eh;
+ struct hci_acl_hdr *ah;
+ struct hci_sco_hdr *sh;
+
+
+ switch (info->rx_state) {
+
+ case RECV_WAIT_EVENT_HEADER:
+ eh = hci_event_hdr(info->rx_skb);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = eh->plen;
+ break;
+
+ case RECV_WAIT_ACL_HEADER:
+ ah = hci_acl_hdr(info->rx_skb);
+ dlen = __le16_to_cpu(ah->dlen);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = dlen;
+ break;
+
+ case RECV_WAIT_SCO_HEADER:
+ sh = hci_sco_hdr(info->rx_skb);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = sh->dlen;
+ break;
+
+ case RECV_WAIT_DATA:
+ hci_recv_frame(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+
+ }
+
+ }
+
+ }
+
+ /* Make sure we don't stay here too long */
+ if (boguscount++ > 16)
+ break;
+
+ } while (inb(iobase + UART_LSR) & UART_LSR_DR);
+}
+
+
+static irqreturn_t btuart_interrupt(int irq, void *dev_inst)
+{
+ btuart_info_t *info = dev_inst;
+ unsigned int iobase;
+ int boguscount = 0;
+ int iir, lsr;
+ irqreturn_t r = IRQ_NONE;
+
+ if (!info || !info->hdev)
+ /* our irq handler is shared */
+ return IRQ_NONE;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ iobase = info->p_dev->resource[0]->start;
+#else
+ iobase = info->p_dev->io.BasePort1;
+#endif
+
+ spin_lock(&(info->lock));
+
+ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+ while (iir) {
+ r = IRQ_HANDLED;
+
+ /* Clear interrupt */
+ lsr = inb(iobase + UART_LSR);
+
+ switch (iir) {
+ case UART_IIR_RLSI:
+ BT_ERR("RLSI");
+ break;
+ case UART_IIR_RDI:
+ /* Receive interrupt */
+ btuart_receive(info);
+ break;
+ case UART_IIR_THRI:
+ if (lsr & UART_LSR_THRE) {
+ /* Transmitter ready for data */
+ btuart_write_wakeup(info);
+ }
+ break;
+ default:
+ BT_ERR("Unhandled IIR=%#x", iir);
+ break;
+ }
+
+ /* Make sure we don't stay here too long */
+ if (boguscount++ > 100)
+ break;
+
+ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+
+ }
+
+ spin_unlock(&(info->lock));
+
+ return r;
+}
+
+
+static void btuart_change_speed(btuart_info_t *info, unsigned int speed)
+{
+ unsigned long flags;
+ unsigned int iobase;
+ int fcr; /* FIFO control reg */
+ int lcr; /* Line control reg */
+ int divisor;
+
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ iobase = info->p_dev->resource[0]->start;
+#else
+ iobase = info->p_dev->io.BasePort1;
+#endif
+
+ spin_lock_irqsave(&(info->lock), flags);
+
+ /* Turn off interrupts */
+ outb(0, iobase + UART_IER);
+
+ divisor = SPEED_MAX / speed;
+
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT;
+
+ /*
+ * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and
+ * almost 1,7 ms at 19200 bps. At speeds above that we can just forget
+ * about this timeout since it will always be fast enough.
+ */
+
+ if (speed < 38400)
+ fcr |= UART_FCR_TRIGGER_1;
+ else
+ fcr |= UART_FCR_TRIGGER_14;
+
+ /* Bluetooth cards use 8N1 */
+ lcr = UART_LCR_WLEN8;
+
+ outb(UART_LCR_DLAB | lcr, iobase + UART_LCR); /* Set DLAB */
+ outb(divisor & 0xff, iobase + UART_DLL); /* Set speed */
+ outb(divisor >> 8, iobase + UART_DLM);
+ outb(lcr, iobase + UART_LCR); /* Set 8N1 */
+ outb(fcr, iobase + UART_FCR); /* Enable FIFO's */
+
+ /* Turn on interrupts */
+ outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
+
+ spin_unlock_irqrestore(&(info->lock), flags);
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int btuart_hci_flush(struct hci_dev *hdev)
+{
+ btuart_info_t *info = (btuart_info_t *)(hdev->driver_data);
+
+ /* Drop TX queue */
+ skb_queue_purge(&(info->txq));
+
+ return 0;
+}
+
+
+static int btuart_hci_open(struct hci_dev *hdev)
+{
+ set_bit(HCI_RUNNING, &(hdev->flags));
+
+ return 0;
+}
+
+
+static int btuart_hci_close(struct hci_dev *hdev)
+{
+ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+ return 0;
+
+ btuart_hci_flush(hdev);
+
+ return 0;
+}
+
+
+static int btuart_hci_send_frame(struct sk_buff *skb)
+{
+ btuart_info_t *info;
+ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+
+ if (!hdev) {
+ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+ info = (btuart_info_t *)(hdev->driver_data);
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+ };
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ skb_queue_tail(&(info->txq), skb);
+
+ btuart_write_wakeup(info);
+
+ return 0;
+}
+
+
+static void btuart_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int btuart_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+static int btuart_open(btuart_info_t *info)
+{
+ unsigned long flags;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+ unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+ struct hci_dev *hdev;
+
+ spin_lock_init(&(info->lock));
+
+ skb_queue_head_init(&(info->txq));
+
+ info->rx_state = RECV_WAIT_PACKET_TYPE;
+ info->rx_count = 0;
+ info->rx_skb = NULL;
+
+ /* Initialize HCI device */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BT_ERR("Can't allocate HCI device");
+ return -ENOMEM;
+ }
+
+ info->hdev = hdev;
+
+ hdev->bus = HCI_PCCARD;
+ hdev->driver_data = info;
+ SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
+
+ hdev->open = btuart_hci_open;
+ hdev->close = btuart_hci_close;
+ hdev->flush = btuart_hci_flush;
+ hdev->send = btuart_hci_send_frame;
+ hdev->destruct = btuart_hci_destruct;
+ hdev->ioctl = btuart_hci_ioctl;
+
+ hdev->owner = THIS_MODULE;
+
+ spin_lock_irqsave(&(info->lock), flags);
+
+ /* Reset UART */
+ outb(0, iobase + UART_MCR);
+
+ /* Turn off interrupts */
+ outb(0, iobase + UART_IER);
+
+ /* Initialize UART */
+ outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */
+ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
+
+ /* Turn on interrupts */
+ // outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
+
+ spin_unlock_irqrestore(&(info->lock), flags);
+
+ btuart_change_speed(info, DEFAULT_BAUD_RATE);
+
+ /* Timeout before it is safe to send the first HCI packet */
+ msleep(1000);
+
+ /* Register HCI device */
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ info->hdev = NULL;
+ hci_free_dev(hdev);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+
+static int btuart_close(btuart_info_t *info)
+{
+ unsigned long flags;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+ unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+ struct hci_dev *hdev = info->hdev;
+
+ if (!hdev)
+ return -ENODEV;
+
+ btuart_hci_close(hdev);
+
+ spin_lock_irqsave(&(info->lock), flags);
+
+ /* Reset UART */
+ outb(0, iobase + UART_MCR);
+
+ /* Turn off interrupts */
+ outb(0, iobase + UART_IER);
+
+ spin_unlock_irqrestore(&(info->lock), flags);
+
+ hci_unregister_dev(hdev);
+ hci_free_dev(hdev);
+
+ return 0;
+}
+
+static int btuart_probe(struct pcmcia_device *link)
+{
+ btuart_info_t *info;
+
+ /* Create new info device */
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->p_dev = link;
+ link->priv = info;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37))
+ link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP |
+ CONF_AUTO_SET_IO;
+#else
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+ link->resource[0]->end = 8;
+#else
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.NumPorts1= 8;
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35))
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+
+ link->irq.Handler = btuart_interrupt;
+#endif
+
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+#endif
+
+ return btuart_config(link);
+}
+
+
+static void btuart_detach(struct pcmcia_device *link)
+{
+ btuart_info_t *info = link->priv;
+
+ btuart_release(link);
+ kfree(info);
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37))
+static int btuart_check_config(struct pcmcia_device *p_dev, void *priv_data)
+{
+ int *try = priv_data;
+
+ if (try == 0)
+ p_dev->io_lines = 16;
+
+ if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0))
+ return -EINVAL;
+
+ p_dev->resource[0]->end = 8;
+ p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+ p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+
+ return pcmcia_request_io(p_dev);
+}
+
+static int btuart_check_config_notpicky(struct pcmcia_device *p_dev,
+ void *priv_data)
+{
+ static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
+ int j;
+
+ if (p_dev->io_lines > 3)
+ return -ENODEV;
+
+ p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+ p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+ p_dev->resource[0]->end = 8;
+
+ for (j = 0; j < 5; j++) {
+ p_dev->resource[0]->start = base[j];
+ p_dev->io_lines = base[j] ? 16 : 3;
+ if (!pcmcia_request_io(p_dev))
+ return 0;
+ }
+ return -ENODEV;
+}
+#else
+static int btuart_check_config(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ int *try = priv_data;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ p_dev->io_lines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK;
+#endif
+
+ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) &&
+ (cf->io.win[0].base != 0)) {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ p_dev->resource[0]->start = cf->io.win[0].base;
+ if (!pcmcia_request_io(p_dev))
+#else
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.IOAddrLines = (*try == 0) ? 16 :
+ cf->io.flags & CISTPL_IO_LINES_MASK;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+#endif
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static int btuart_check_config_notpicky(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
+ int j;
+
+ if ((cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
+ for (j = 0; j < 5; j++) {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ p_dev->resource[0]->start = base[j];
+ p_dev->io_lines = base[j] ? 16 : 3;
+ if (!pcmcia_request_io(p_dev))
+#else
+ p_dev->io.BasePort1 = base[j];
+ p_dev->io.IOAddrLines = base[j] ? 16 : 3;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+#endif
+ return 0;
+ }
+ }
+ return -ENODEV;
+}
+#endif
+
+static int btuart_config(struct pcmcia_device *link)
+{
+ btuart_info_t *info = link->priv;
+ int i;
+ int try;
+
+ /* First pass: look for a config entry that looks normal.
+ Two tries: without IO aliases, then with aliases */
+ for (try = 0; try < 2; try++)
+ if (!pcmcia_loop_config(link, btuart_check_config, &try))
+ goto found_port;
+
+ /* Second pass: try to find an entry that isn't picky about
+ its base address, then try to grab any standard serial port
+ address, and finally try to get any free port. */
+ if (!pcmcia_loop_config(link, btuart_check_config_notpicky, NULL))
+ goto found_port;
+
+ BT_ERR("No usable port range found");
+ goto failed;
+
+found_port:
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ i = pcmcia_request_irq(link, btuart_interrupt);
+ if (i != 0)
+ goto failed;
+#else
+ i = pcmcia_request_irq(link, &link->irq);
+ if (i != 0)
+ link->irq.AssignedIRQ = 0;
+#endif
+
+ i = pcmcia_enable_device(link);
+ if (i != 0)
+ goto failed;
+
+ if (btuart_open(info) != 0)
+ goto failed;
+
+ return 0;
+
+failed:
+ btuart_release(link);
+ return -ENODEV;
+}
+
+
+static void btuart_release(struct pcmcia_device *link)
+{
+ btuart_info_t *info = link->priv;
+
+ btuart_close(info);
+
+ pcmcia_disable_device(link);
+}
+
+static const struct pcmcia_device_id btuart_ids[] = {
+ /* don't use this driver. Use serial_cs + hci_uart instead */
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, btuart_ids);
+
+static struct pcmcia_driver btuart_driver = {
+ .owner = THIS_MODULE,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37))
+ .name = "btuart_cs",
+#else
+ .drv = {
+ .name = "btuart_cs",
+ },
+#endif
+ .probe = btuart_probe,
+ .remove = btuart_detach,
+ .id_table = btuart_ids,
+};
+
+static int __init init_btuart_cs(void)
+{
+ return pcmcia_register_driver(&btuart_driver);
+}
+
+
+static void __exit exit_btuart_cs(void)
+{
+ pcmcia_unregister_driver(&btuart_driver);
+}
+
+module_init(init_btuart_cs);
+module_exit(exit_btuart_cs);
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
new file mode 100644
index 0000000..2bd87d4
--- /dev/null
+++ b/drivers/bluetooth/btusb.c
@@ -0,0 +1,1270 @@
+/*
+ *
+ * Generic Bluetooth USB driver
+ *
+ * Copyright (C) 2005-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#define VERSION "0.6"
+
+static int ignore_dga;
+static int ignore_csr;
+static int ignore_sniffer;
+static int disable_scofix;
+static int force_scofix;
+
+static int reset = 1;
+
+static struct usb_driver btusb_driver;
+
+#define BTUSB_IGNORE 0x01
+#define BTUSB_DIGIANSWER 0x02
+#define BTUSB_CSR 0x04
+#define BTUSB_SNIFFER 0x08
+#define BTUSB_BCM92035 0x10
+#define BTUSB_BROKEN_ISOC 0x20
+#define BTUSB_WRONG_SCO_MTU 0x40
+#define BTUSB_ATH3012 0x80
+
+static struct usb_device_id btusb_table[] = {
+ /* Generic Bluetooth USB device */
+ { USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
+
+ /* Broadcom SoftSailing reporting vendor specific */
+ { USB_DEVICE(0x05ac, 0x21e1) },
+
+ /* Apple MacBookPro 7,1 */
+ { USB_DEVICE(0x05ac, 0x8213) },
+
+ /* Apple iMac11,1 */
+ { USB_DEVICE(0x05ac, 0x8215) },
+
+ /* Apple MacBookPro6,2 */
+ { USB_DEVICE(0x05ac, 0x8218) },
+
+ /* Apple MacBookAir3,1, MacBookAir3,2 */
+ { USB_DEVICE(0x05ac, 0x821b) },
+
+ /* Apple MacBookAir4,1 */
+ { USB_DEVICE(0x05ac, 0x821f) },
+
+ /* Apple MacBookPro8,2 */
+ { USB_DEVICE(0x05ac, 0x821a) },
+
+ /* Apple MacMini5,1 */
+ { USB_DEVICE(0x05ac, 0x8281) },
+
+ /* AVM BlueFRITZ! USB v2.0 */
+ { USB_DEVICE(0x057c, 0x3800) },
+
+ /* Bluetooth Ultraport Module from IBM */
+ { USB_DEVICE(0x04bf, 0x030a) },
+
+ /* ALPS Modules with non-standard id */
+ { USB_DEVICE(0x044e, 0x3001) },
+ { USB_DEVICE(0x044e, 0x3002) },
+
+ /* Ericsson with non-standard id */
+ { USB_DEVICE(0x0bdb, 0x1002) },
+
+ /* Canyon CN-BTU1 with HID interfaces */
+ { USB_DEVICE(0x0c10, 0x0000) },
+
+ /* Broadcom BCM20702A0 */
+ { USB_DEVICE(0x413c, 0x8197) },
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, btusb_table);
+
+static struct usb_device_id blacklist_table[] = {
+ /* CSR BlueCore devices */
+ { USB_DEVICE(0x0a12, 0x0001), .driver_info = BTUSB_CSR },
+
+ /* Broadcom BCM2033 without firmware */
+ { USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE },
+
+ /* Atheros 3011 with sflash firmware */
+ { USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
+ { USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_IGNORE },
+ { USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE },
+
+ /* Atheros AR9285 Malbec with sflash firmware */
+ { USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
+
+ /* Atheros 3012 with sflash firmware */
+ { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
+
+ /* Atheros AR5BBU12 with sflash firmware */
+ { USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
+
+ /* Broadcom BCM2035 */
+ { USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
+
+ /* Broadcom BCM2045 */
+ { USB_DEVICE(0x0a5c, 0x2039), .driver_info = BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x2101), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+ /* IBM/Lenovo ThinkPad with Broadcom chip */
+ { USB_DEVICE(0x0a5c, 0x201e), .driver_info = BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x2110), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+ /* HP laptop with Broadcom chip */
+ { USB_DEVICE(0x03f0, 0x171d), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+ /* Dell laptop with Broadcom chip */
+ { USB_DEVICE(0x413c, 0x8126), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+ /* Dell Wireless 370 and 410 devices */
+ { USB_DEVICE(0x413c, 0x8152), .driver_info = BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x413c, 0x8156), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+ /* Belkin F8T012 and F8T013 devices */
+ { USB_DEVICE(0x050d, 0x0012), .driver_info = BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x050d, 0x0013), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+ /* Asus WL-BTD202 device */
+ { USB_DEVICE(0x0b05, 0x1715), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+ /* Kensington Bluetooth USB adapter */
+ { USB_DEVICE(0x047d, 0x105e), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+ /* RTX Telecom based adapters with buggy SCO support */
+ { USB_DEVICE(0x0400, 0x0807), .driver_info = BTUSB_BROKEN_ISOC },
+ { USB_DEVICE(0x0400, 0x080a), .driver_info = BTUSB_BROKEN_ISOC },
+
+ /* CONWISE Technology based adapters with buggy SCO support */
+ { USB_DEVICE(0x0e5e, 0x6622), .driver_info = BTUSB_BROKEN_ISOC },
+
+ /* Digianswer devices */
+ { USB_DEVICE(0x08fd, 0x0001), .driver_info = BTUSB_DIGIANSWER },
+ { USB_DEVICE(0x08fd, 0x0002), .driver_info = BTUSB_IGNORE },
+
+ /* CSR BlueCore Bluetooth Sniffer */
+ { USB_DEVICE(0x0a12, 0x0002), .driver_info = BTUSB_SNIFFER },
+
+ /* Frontline ComProbe Bluetooth Sniffer */
+ { USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER },
+
+ { } /* Terminating entry */
+};
+
+#define BTUSB_MAX_ISOC_FRAMES 10
+
+#define BTUSB_INTR_RUNNING 0
+#define BTUSB_BULK_RUNNING 1
+#define BTUSB_ISOC_RUNNING 2
+#define BTUSB_SUSPENDING 3
+#define BTUSB_DID_ISO_RESUME 4
+
+struct btusb_data {
+ struct hci_dev *hdev;
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ struct usb_interface *isoc;
+
+ spinlock_t lock;
+
+ unsigned long flags;
+
+ struct work_struct work;
+ struct work_struct waker;
+
+ struct usb_anchor tx_anchor;
+ struct usb_anchor intr_anchor;
+ struct usb_anchor bulk_anchor;
+ struct usb_anchor isoc_anchor;
+ struct usb_anchor deferred;
+ int tx_in_flight;
+ spinlock_t txlock;
+
+ struct usb_endpoint_descriptor *intr_ep;
+ struct usb_endpoint_descriptor *bulk_tx_ep;
+ struct usb_endpoint_descriptor *bulk_rx_ep;
+ struct usb_endpoint_descriptor *isoc_tx_ep;
+ struct usb_endpoint_descriptor *isoc_rx_ep;
+
+ __u8 cmdreq_type;
+
+ unsigned int sco_num;
+ int isoc_altsetting;
+ int suspend_count;
+};
+
+static int inc_tx(struct btusb_data *data)
+{
+ unsigned long flags;
+ int rv;
+
+ spin_lock_irqsave(&data->txlock, flags);
+ rv = test_bit(BTUSB_SUSPENDING, &data->flags);
+ if (!rv)
+ data->tx_in_flight++;
+ spin_unlock_irqrestore(&data->txlock, flags);
+
+ return rv;
+}
+
+static void btusb_intr_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = urb->context;
+ struct btusb_data *data = hdev->driver_data;
+ int err;
+
+ BT_DBG("%s urb %p status %d count %d", hdev->name,
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return;
+
+ if (urb->status == 0) {
+ hdev->stat.byte_rx += urb->actual_length;
+
+ if (hci_recv_fragment(hdev, HCI_EVENT_PKT,
+ urb->transfer_buffer,
+ urb->actual_length) < 0) {
+ BT_ERR("%s corrupted event packet", hdev->name);
+ hdev->stat.err_rx++;
+ }
+ }
+
+ if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
+ return;
+
+ usb_mark_last_busy(data->udev);
+ usb_anchor_urb(urb, &data->intr_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ /* -EPERM: urb is being killed;
+ * -ENODEV: device got disconnected */
+ if (err != -EPERM && err != -ENODEV)
+ BT_ERR("%s urb %p failed to resubmit (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btusb_data *data = hdev->driver_data;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!data->intr_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+
+ size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
+
+ buf = kmalloc(size, mem_flags);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvintpipe(data->udev, data->intr_ep->bEndpointAddress);
+
+ usb_fill_int_urb(urb, data->udev, pipe, buf, size,
+ btusb_intr_complete, hdev,
+ data->intr_ep->bInterval);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_anchor_urb(urb, &data->intr_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BT_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_bulk_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = urb->context;
+ struct btusb_data *data = hdev->driver_data;
+ int err;
+
+ BT_DBG("%s urb %p status %d count %d", hdev->name,
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return;
+
+ if (urb->status == 0) {
+ hdev->stat.byte_rx += urb->actual_length;
+
+ if (hci_recv_fragment(hdev, HCI_ACLDATA_PKT,
+ urb->transfer_buffer,
+ urb->actual_length) < 0) {
+ BT_ERR("%s corrupted ACL packet", hdev->name);
+ hdev->stat.err_rx++;
+ }
+ }
+
+ if (!test_bit(BTUSB_BULK_RUNNING, &data->flags))
+ return;
+
+ usb_anchor_urb(urb, &data->bulk_anchor);
+ usb_mark_last_busy(data->udev);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ /* -EPERM: urb is being killed;
+ * -ENODEV: device got disconnected */
+ if (err != -EPERM && err != -ENODEV)
+ BT_ERR("%s urb %p failed to resubmit (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btusb_data *data = hdev->driver_data;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size = HCI_MAX_FRAME_SIZE;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!data->bulk_rx_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+
+ buf = kmalloc(size, mem_flags);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
+
+ usb_fill_bulk_urb(urb, data->udev, pipe,
+ buf, size, btusb_bulk_complete, hdev);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_mark_last_busy(data->udev);
+ usb_anchor_urb(urb, &data->bulk_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BT_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_isoc_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = urb->context;
+ struct btusb_data *data = hdev->driver_data;
+ int i, err;
+
+ BT_DBG("%s urb %p status %d count %d", hdev->name,
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return;
+
+ if (urb->status == 0) {
+ for (i = 0; i < urb->number_of_packets; i++) {
+ unsigned int offset = urb->iso_frame_desc[i].offset;
+ unsigned int length = urb->iso_frame_desc[i].actual_length;
+
+ if (urb->iso_frame_desc[i].status)
+ continue;
+
+ hdev->stat.byte_rx += length;
+
+ if (hci_recv_fragment(hdev, HCI_SCODATA_PKT,
+ urb->transfer_buffer + offset,
+ length) < 0) {
+ BT_ERR("%s corrupted SCO packet", hdev->name);
+ hdev->stat.err_rx++;
+ }
+ }
+ }
+
+ if (!test_bit(BTUSB_ISOC_RUNNING, &data->flags))
+ return;
+
+ usb_anchor_urb(urb, &data->isoc_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ /* -EPERM: urb is being killed;
+ * -ENODEV: device got disconnected */
+ if (err != -EPERM && err != -ENODEV)
+ BT_ERR("%s urb %p failed to resubmit (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
+{
+ int i, offset = 0;
+
+ BT_DBG("len %d mtu %d", len, mtu);
+
+ for (i = 0; i < BTUSB_MAX_ISOC_FRAMES && len >= mtu;
+ i++, offset += mtu, len -= mtu) {
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = mtu;
+ }
+
+ if (len && i < BTUSB_MAX_ISOC_FRAMES) {
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = len;
+ i++;
+ }
+
+ urb->number_of_packets = i;
+}
+
+static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btusb_data *data = hdev->driver_data;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!data->isoc_rx_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+
+ size = le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize) *
+ BTUSB_MAX_ISOC_FRAMES;
+
+ buf = kmalloc(size, mem_flags);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress);
+
+ urb->dev = data->udev;
+ urb->pipe = pipe;
+ urb->context = hdev;
+ urb->complete = btusb_isoc_complete;
+ urb->interval = data->isoc_rx_ep->bInterval;
+
+ urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP;
+ urb->transfer_buffer = buf;
+ urb->transfer_buffer_length = size;
+
+ __fill_isoc_descriptor(urb, size,
+ le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
+
+ usb_anchor_urb(urb, &data->isoc_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BT_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_tx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct btusb_data *data = hdev->driver_data;
+
+ BT_DBG("%s urb %p status %d count %d", hdev->name,
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ goto done;
+
+ if (!urb->status)
+ hdev->stat.byte_tx += urb->transfer_buffer_length;
+ else
+ hdev->stat.err_tx++;
+
+done:
+ spin_lock(&data->txlock);
+ data->tx_in_flight--;
+ spin_unlock(&data->txlock);
+
+ kfree(urb->setup_packet);
+
+ kfree_skb(skb);
+}
+
+static void btusb_isoc_tx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+
+ BT_DBG("%s urb %p status %d count %d", hdev->name,
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ goto done;
+
+ if (!urb->status)
+ hdev->stat.byte_tx += urb->transfer_buffer_length;
+ else
+ hdev->stat.err_tx++;
+
+done:
+ kfree(urb->setup_packet);
+
+ kfree_skb(skb);
+}
+
+static int btusb_open(struct hci_dev *hdev)
+{
+ struct btusb_data *data = hdev->driver_data;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ err = usb_autopm_get_interface(data->intf);
+ if (err < 0)
+ return err;
+
+ data->intf->needs_remote_wakeup = 1;
+
+ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+ goto done;
+
+ if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
+ goto done;
+
+ err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
+ if (err < 0)
+ goto failed;
+
+ err = btusb_submit_bulk_urb(hdev, GFP_KERNEL);
+ if (err < 0) {
+ usb_kill_anchored_urbs(&data->intr_anchor);
+ goto failed;
+ }
+
+ set_bit(BTUSB_BULK_RUNNING, &data->flags);
+ btusb_submit_bulk_urb(hdev, GFP_KERNEL);
+
+done:
+ usb_autopm_put_interface(data->intf);
+ return 0;
+
+failed:
+ clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ usb_autopm_put_interface(data->intf);
+ return err;
+}
+
+static void btusb_stop_traffic(struct btusb_data *data)
+{
+ usb_kill_anchored_urbs(&data->intr_anchor);
+ usb_kill_anchored_urbs(&data->bulk_anchor);
+ usb_kill_anchored_urbs(&data->isoc_anchor);
+}
+
+static int btusb_close(struct hci_dev *hdev)
+{
+ struct btusb_data *data = hdev->driver_data;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ cancel_work_sync(&data->work);
+ cancel_work_sync(&data->waker);
+
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+ clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+
+ btusb_stop_traffic(data);
+ err = usb_autopm_get_interface(data->intf);
+ if (err < 0)
+ goto failed;
+
+ data->intf->needs_remote_wakeup = 0;
+ usb_autopm_put_interface(data->intf);
+
+failed:
+ usb_scuttle_anchored_urbs(&data->deferred);
+ return 0;
+}
+
+static int btusb_flush(struct hci_dev *hdev)
+{
+ struct btusb_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ usb_kill_anchored_urbs(&data->tx_anchor);
+
+ return 0;
+}
+
+static int btusb_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct btusb_data *data = hdev->driver_data;
+ struct usb_ctrlrequest *dr;
+ struct urb *urb;
+ unsigned int pipe;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+
+ dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
+ if (!dr) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ dr->bRequestType = data->cmdreq_type;
+ dr->bRequest = 0;
+ dr->wIndex = 0;
+ dr->wValue = 0;
+ dr->wLength = __cpu_to_le16(skb->len);
+
+ pipe = usb_sndctrlpipe(data->udev, 0x00);
+
+ usb_fill_control_urb(urb, data->udev, pipe, (void *) dr,
+ skb->data, skb->len, btusb_tx_complete, skb);
+
+ hdev->stat.cmd_tx++;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ if (!data->bulk_tx_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+
+ pipe = usb_sndbulkpipe(data->udev,
+ data->bulk_tx_ep->bEndpointAddress);
+
+ usb_fill_bulk_urb(urb, data->udev, pipe,
+ skb->data, skb->len, btusb_tx_complete, skb);
+
+ if (skb->priority >= HCI_PRIO_MAX - 1)
+ urb->transfer_flags = URB_ISO_ASAP;
+
+ hdev->stat.acl_tx++;
+ break;
+
+ case HCI_SCODATA_PKT:
+ if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+
+ pipe = usb_sndisocpipe(data->udev,
+ data->isoc_tx_ep->bEndpointAddress);
+
+ usb_fill_int_urb(urb, data->udev, pipe,
+ skb->data, skb->len, btusb_isoc_tx_complete,
+ skb, data->isoc_tx_ep->bInterval);
+
+ urb->transfer_flags = URB_ISO_ASAP;
+
+ __fill_isoc_descriptor(urb, skb->len,
+ le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
+
+ hdev->stat.sco_tx++;
+ goto skip_waking;
+
+ default:
+ return -EILSEQ;
+ }
+
+ err = inc_tx(data);
+ if (err) {
+ usb_anchor_urb(urb, &data->deferred);
+ schedule_work(&data->waker);
+ err = 0;
+ goto done;
+ }
+
+skip_waking:
+ usb_anchor_urb(urb, &data->tx_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BT_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ kfree(urb->setup_packet);
+ usb_unanchor_urb(urb);
+ } else {
+ usb_mark_last_busy(data->udev);
+ }
+
+ usb_free_urb(urb);
+
+done:
+ return err;
+}
+
+static void btusb_destruct(struct hci_dev *hdev)
+{
+ struct btusb_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ kfree(data);
+}
+
+static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
+{
+ struct btusb_data *data = hdev->driver_data;
+
+ BT_DBG("%s evt %d", hdev->name, evt);
+
+ if (hdev->conn_hash.sco_num != data->sco_num) {
+ data->sco_num = hdev->conn_hash.sco_num;
+ schedule_work(&data->work);
+ }
+}
+
+static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting)
+{
+ struct btusb_data *data = hdev->driver_data;
+ struct usb_interface *intf = data->isoc;
+ struct usb_endpoint_descriptor *ep_desc;
+ int i, err;
+
+ if (!data->isoc)
+ return -ENODEV;
+
+ err = usb_set_interface(data->udev, 1, altsetting);
+ if (err < 0) {
+ BT_ERR("%s setting interface failed (%d)", hdev->name, -err);
+ return err;
+ }
+
+ data->isoc_altsetting = altsetting;
+
+ data->isoc_tx_ep = NULL;
+ data->isoc_rx_ep = NULL;
+
+ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+ ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+ if (!data->isoc_tx_ep && usb_endpoint_is_isoc_out(ep_desc)) {
+ data->isoc_tx_ep = ep_desc;
+ continue;
+ }
+
+ if (!data->isoc_rx_ep && usb_endpoint_is_isoc_in(ep_desc)) {
+ data->isoc_rx_ep = ep_desc;
+ continue;
+ }
+ }
+
+ if (!data->isoc_tx_ep || !data->isoc_rx_ep) {
+ BT_ERR("%s invalid SCO descriptors", hdev->name);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void btusb_work(struct work_struct *work)
+{
+ struct btusb_data *data = container_of(work, struct btusb_data, work);
+ struct hci_dev *hdev = data->hdev;
+ int err;
+
+ if (hdev->conn_hash.sco_num > 0) {
+ if (!test_bit(BTUSB_DID_ISO_RESUME, &data->flags)) {
+ err = usb_autopm_get_interface(data->isoc ? data->isoc : data->intf);
+ if (err < 0) {
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ usb_kill_anchored_urbs(&data->isoc_anchor);
+ return;
+ }
+
+ set_bit(BTUSB_DID_ISO_RESUME, &data->flags);
+ }
+ if (data->isoc_altsetting != 2) {
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ usb_kill_anchored_urbs(&data->isoc_anchor);
+
+ if (__set_isoc_interface(hdev, 2) < 0)
+ return;
+ }
+
+ if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+ if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0)
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ else
+ btusb_submit_isoc_urb(hdev, GFP_KERNEL);
+ }
+ } else {
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ usb_kill_anchored_urbs(&data->isoc_anchor);
+
+ __set_isoc_interface(hdev, 0);
+ if (test_and_clear_bit(BTUSB_DID_ISO_RESUME, &data->flags))
+ usb_autopm_put_interface(data->isoc ? data->isoc : data->intf);
+ }
+}
+
+static void btusb_waker(struct work_struct *work)
+{
+ struct btusb_data *data = container_of(work, struct btusb_data, waker);
+ int err;
+
+ err = usb_autopm_get_interface(data->intf);
+ if (err < 0)
+ return;
+
+ usb_autopm_put_interface(data->intf);
+}
+
+static int btusb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ struct btusb_data *data;
+ struct hci_dev *hdev;
+ int i, err;
+
+ BT_DBG("intf %p id %p", intf, id);
+
+ /* interface numbers are hardcoded in the spec */
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
+
+ if (!id->driver_info) {
+ const struct usb_device_id *match;
+ match = usb_match_id(intf, blacklist_table);
+ if (match)
+ id = match;
+ }
+
+ if (id->driver_info == BTUSB_IGNORE)
+ return -ENODEV;
+
+ if (ignore_dga && id->driver_info & BTUSB_DIGIANSWER)
+ return -ENODEV;
+
+ if (ignore_csr && id->driver_info & BTUSB_CSR)
+ return -ENODEV;
+
+ if (ignore_sniffer && id->driver_info & BTUSB_SNIFFER)
+ return -ENODEV;
+
+ if (id->driver_info & BTUSB_ATH3012) {
+ struct usb_device *udev = interface_to_usbdev(intf);
+
+ /* Old firmware would otherwise let ath3k driver load
+ * patch and sysconfig files */
+ if (le16_to_cpu(udev->descriptor.bcdDevice) <= 0x0001)
+ return -ENODEV;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+ ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+ if (!data->intr_ep && usb_endpoint_is_int_in(ep_desc)) {
+ data->intr_ep = ep_desc;
+ continue;
+ }
+
+ if (!data->bulk_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) {
+ data->bulk_tx_ep = ep_desc;
+ continue;
+ }
+
+ if (!data->bulk_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) {
+ data->bulk_rx_ep = ep_desc;
+ continue;
+ }
+ }
+
+ if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) {
+ kfree(data);
+ return -ENODEV;
+ }
+
+ data->cmdreq_type = USB_TYPE_CLASS;
+
+ data->udev = interface_to_usbdev(intf);
+ data->intf = intf;
+
+ spin_lock_init(&data->lock);
+
+ INIT_WORK(&data->work, btusb_work);
+ INIT_WORK(&data->waker, btusb_waker);
+ spin_lock_init(&data->txlock);
+
+ init_usb_anchor(&data->tx_anchor);
+ init_usb_anchor(&data->intr_anchor);
+ init_usb_anchor(&data->bulk_anchor);
+ init_usb_anchor(&data->isoc_anchor);
+ init_usb_anchor(&data->deferred);
+
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ hdev->bus = HCI_USB;
+ hdev->driver_data = data;
+
+ data->hdev = hdev;
+
+ SET_HCIDEV_DEV(hdev, &intf->dev);
+
+ hdev->open = btusb_open;
+ hdev->close = btusb_close;
+ hdev->flush = btusb_flush;
+ hdev->send = btusb_send_frame;
+ hdev->destruct = btusb_destruct;
+ hdev->notify = btusb_notify;
+
+ hdev->owner = THIS_MODULE;
+
+ /* Interface numbers are hardcoded in the specification */
+ data->isoc = usb_ifnum_to_if(data->udev, 1);
+
+ if (!reset)
+ set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
+
+ if (force_scofix || id->driver_info & BTUSB_WRONG_SCO_MTU) {
+ if (!disable_scofix)
+ set_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks);
+ }
+
+ if (id->driver_info & BTUSB_BROKEN_ISOC)
+ data->isoc = NULL;
+
+ if (id->driver_info & BTUSB_DIGIANSWER) {
+ data->cmdreq_type = USB_TYPE_VENDOR;
+ set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
+ }
+
+ if (id->driver_info & BTUSB_CSR) {
+ struct usb_device *udev = data->udev;
+
+ /* Old firmware would otherwise execute USB reset */
+ if (le16_to_cpu(udev->descriptor.bcdDevice) < 0x117)
+ set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
+ }
+
+ if (id->driver_info & BTUSB_SNIFFER) {
+ struct usb_device *udev = data->udev;
+
+ /* New sniffer firmware has crippled HCI interface */
+ if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997)
+ set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
+
+ data->isoc = NULL;
+ }
+
+ if (id->driver_info & BTUSB_BCM92035) {
+ unsigned char cmd[] = { 0x3b, 0xfc, 0x01, 0x00 };
+ struct sk_buff *skb;
+
+ skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
+ if (skb) {
+ memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
+ skb_queue_tail(&hdev->driver_init, skb);
+ }
+ }
+
+ if (data->isoc) {
+ err = usb_driver_claim_interface(&btusb_driver,
+ data->isoc, data);
+ if (err < 0) {
+ hci_free_dev(hdev);
+ kfree(data);
+ return err;
+ }
+ }
+
+ err = hci_register_dev(hdev);
+ if (err < 0) {
+ hci_free_dev(hdev);
+ kfree(data);
+ return err;
+ }
+
+ usb_set_intfdata(intf, data);
+
+ return 0;
+}
+
+static void btusb_disconnect(struct usb_interface *intf)
+{
+ struct btusb_data *data = usb_get_intfdata(intf);
+ struct hci_dev *hdev;
+
+ BT_DBG("intf %p", intf);
+
+ if (!data)
+ return;
+
+ hdev = data->hdev;
+
+ __hci_dev_hold(hdev);
+
+ usb_set_intfdata(data->intf, NULL);
+
+ if (data->isoc)
+ usb_set_intfdata(data->isoc, NULL);
+
+ hci_unregister_dev(hdev);
+
+ if (intf == data->isoc)
+ usb_driver_release_interface(&btusb_driver, data->intf);
+ else if (data->isoc)
+ usb_driver_release_interface(&btusb_driver, data->isoc);
+
+ __hci_dev_put(hdev);
+
+ hci_free_dev(hdev);
+}
+
+#ifdef CONFIG_PM
+static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct btusb_data *data = usb_get_intfdata(intf);
+
+ BT_DBG("intf %p", intf);
+
+ if (data->suspend_count++)
+ return 0;
+
+ spin_lock_irq(&data->txlock);
+ if (!(PMSG_IS_AUTO(message) && data->tx_in_flight)) {
+ set_bit(BTUSB_SUSPENDING, &data->flags);
+ spin_unlock_irq(&data->txlock);
+ } else {
+ spin_unlock_irq(&data->txlock);
+ data->suspend_count--;
+ return -EBUSY;
+ }
+
+ cancel_work_sync(&data->work);
+
+ btusb_stop_traffic(data);
+ usb_kill_anchored_urbs(&data->tx_anchor);
+
+ return 0;
+}
+
+static void play_deferred(struct btusb_data *data)
+{
+ struct urb *urb;
+ int err;
+
+ while ((urb = usb_get_from_anchor(&data->deferred))) {
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0)
+ break;
+
+ data->tx_in_flight++;
+ }
+ usb_scuttle_anchored_urbs(&data->deferred);
+}
+
+static int btusb_resume(struct usb_interface *intf)
+{
+ struct btusb_data *data = usb_get_intfdata(intf);
+ struct hci_dev *hdev = data->hdev;
+ int err = 0;
+
+ BT_DBG("intf %p", intf);
+
+ if (--data->suspend_count)
+ return 0;
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ goto done;
+
+ if (test_bit(BTUSB_INTR_RUNNING, &data->flags)) {
+ err = btusb_submit_intr_urb(hdev, GFP_NOIO);
+ if (err < 0) {
+ clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+ goto failed;
+ }
+ }
+
+ if (test_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+ err = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+ if (err < 0) {
+ clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+ goto failed;
+ }
+
+ btusb_submit_bulk_urb(hdev, GFP_NOIO);
+ }
+
+ if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+ if (btusb_submit_isoc_urb(hdev, GFP_NOIO) < 0)
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ else
+ btusb_submit_isoc_urb(hdev, GFP_NOIO);
+ }
+
+ spin_lock_irq(&data->txlock);
+ play_deferred(data);
+ clear_bit(BTUSB_SUSPENDING, &data->flags);
+ spin_unlock_irq(&data->txlock);
+ schedule_work(&data->work);
+
+ return 0;
+
+failed:
+ usb_scuttle_anchored_urbs(&data->deferred);
+done:
+ spin_lock_irq(&data->txlock);
+ clear_bit(BTUSB_SUSPENDING, &data->flags);
+ spin_unlock_irq(&data->txlock);
+
+ return err;
+}
+#endif
+
+static struct usb_driver btusb_driver = {
+ .name = "btusb",
+ .probe = btusb_probe,
+ .disconnect = btusb_disconnect,
+#ifdef CONFIG_PM
+ .suspend = btusb_suspend,
+ .resume = btusb_resume,
+#endif
+ .id_table = btusb_table,
+ .supports_autosuspend = 1,
+};
+
+static int __init btusb_init(void)
+{
+ BT_INFO("Generic Bluetooth USB driver ver %s", VERSION);
+
+ return usb_register(&btusb_driver);
+}
+
+static void __exit btusb_exit(void)
+{
+ usb_deregister(&btusb_driver);
+}
+
+module_init(btusb_init);
+module_exit(btusb_exit);
+
+module_param(ignore_dga, bool, 0644);
+MODULE_PARM_DESC(ignore_dga, "Ignore devices with id 08fd:0001");
+
+module_param(ignore_csr, bool, 0644);
+MODULE_PARM_DESC(ignore_csr, "Ignore devices with id 0a12:0001");
+
+module_param(ignore_sniffer, bool, 0644);
+MODULE_PARM_DESC(ignore_sniffer, "Ignore devices with id 0a12:0002");
+
+module_param(disable_scofix, bool, 0644);
+MODULE_PARM_DESC(disable_scofix, "Disable fixup of wrong SCO buffer size");
+
+module_param(force_scofix, bool, 0644);
+MODULE_PARM_DESC(force_scofix, "Force fixup of wrong SCO buffers size");
+
+module_param(reset, bool, 0644);
+MODULE_PARM_DESC(reset, "Send HCI reset command on initialization");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Generic Bluetooth USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c
new file mode 100644
index 0000000..b5f83b4
--- /dev/null
+++ b/drivers/bluetooth/btwilink.c
@@ -0,0 +1,396 @@
+/*
+ * Texas Instrument's Bluetooth Driver For Shared Transport.
+ *
+ * Bluetooth Driver acts as interface between HCI core and
+ * TI Shared Transport Layer.
+ *
+ * Copyright (C) 2009-2010 Texas Instruments
+ * Author: Raja Mani <raja_mani@ti.com>
+ * Pavan Savoy <pavan_savoy@ti.com>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#define DEBUG
+#include <linux/platform_device.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+
+#include <linux/ti_wilink_st.h>
+#include <linux/module.h>
+
+/* Bluetooth Driver Version */
+#define VERSION "1.0"
+#define MAX_BT_CHNL_IDS 3
+
+/* Number of seconds to wait for registration completion
+ * when ST returns PENDING status.
+ */
+#define BT_REGISTER_TIMEOUT 6000 /* 6 sec */
+
+/**
+ * struct ti_st - driver operation structure
+ * @hdev: hci device pointer which binds to bt driver
+ * @reg_status: ST registration callback status
+ * @st_write: write function provided by the ST driver
+ * to be used by the driver during send_frame.
+ * @wait_reg_completion - completion sync between ti_st_open
+ * and st_reg_completion_cb.
+ */
+struct ti_st {
+ struct hci_dev *hdev;
+ char reg_status;
+ long (*st_write) (struct sk_buff *);
+ struct completion wait_reg_completion;
+};
+
+/* Increments HCI counters based on pocket ID (cmd,acl,sco) */
+static inline void ti_st_tx_complete(struct ti_st *hst, int pkt_type)
+{
+ struct hci_dev *hdev = hst->hdev;
+
+ /* Update HCI stat counters */
+ switch (pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+ }
+}
+
+/* ------- Interfaces to Shared Transport ------ */
+
+/* Called by ST layer to indicate protocol registration completion
+ * status.ti_st_open() function will wait for signal from this
+ * API when st_register() function returns ST_PENDING.
+ */
+static void st_reg_completion_cb(void *priv_data, char data)
+{
+ struct ti_st *lhst = priv_data;
+
+ /* Save registration status for use in ti_st_open() */
+ lhst->reg_status = data;
+ /* complete the wait in ti_st_open() */
+ complete(&lhst->wait_reg_completion);
+}
+
+/* Called by Shared Transport layer when receive data is
+ * available */
+static long st_receive(void *priv_data, struct sk_buff *skb)
+{
+ struct ti_st *lhst = priv_data;
+ int err;
+
+ if (!skb)
+ return -EFAULT;
+
+ if (!lhst) {
+ kfree_skb(skb);
+ return -EFAULT;
+ }
+
+ skb->dev = (void *) lhst->hdev;
+
+ /* Forward skb to HCI core layer */
+ err = hci_recv_frame(skb);
+ if (err < 0) {
+ BT_ERR("Unable to push skb to HCI core(%d)", err);
+ return err;
+ }
+
+ lhst->hdev->stat.byte_rx += skb->len;
+
+ return 0;
+}
+
+/* ------- Interfaces to HCI layer ------ */
+/* protocol structure registered with shared transport */
+static struct st_proto_s ti_st_proto[MAX_BT_CHNL_IDS] = {
+ {
+ .chnl_id = HCI_EVENT_PKT, /* HCI Events */
+ .hdr_len = sizeof(struct hci_event_hdr),
+ .offset_len_in_hdr = offsetof(struct hci_event_hdr, plen),
+ .len_size = 1, /* sizeof(plen) in struct hci_event_hdr */
+ .reserve = 8,
+ },
+ {
+ .chnl_id = HCI_ACLDATA_PKT, /* ACL */
+ .hdr_len = sizeof(struct hci_acl_hdr),
+ .offset_len_in_hdr = offsetof(struct hci_acl_hdr, dlen),
+ .len_size = 2, /* sizeof(dlen) in struct hci_acl_hdr */
+ .reserve = 8,
+ },
+ {
+ .chnl_id = HCI_SCODATA_PKT, /* SCO */
+ .hdr_len = sizeof(struct hci_sco_hdr),
+ .offset_len_in_hdr = offsetof(struct hci_sco_hdr, dlen),
+ .len_size = 1, /* sizeof(dlen) in struct hci_sco_hdr */
+ .reserve = 8,
+ },
+};
+
+/* Called from HCI core to initialize the device */
+static int ti_st_open(struct hci_dev *hdev)
+{
+ unsigned long timeleft;
+ struct ti_st *hst;
+ int err, i;
+
+ BT_DBG("%s %p", hdev->name, hdev);
+
+ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ /* provide contexts for callbacks from ST */
+ hst = hdev->driver_data;
+
+ for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
+ ti_st_proto[i].priv_data = hst;
+ ti_st_proto[i].max_frame_size = HCI_MAX_FRAME_SIZE;
+ ti_st_proto[i].recv = st_receive;
+ ti_st_proto[i].reg_complete_cb = st_reg_completion_cb;
+
+ /* Prepare wait-for-completion handler */
+ init_completion(&hst->wait_reg_completion);
+ /* Reset ST registration callback status flag,
+ * this value will be updated in
+ * st_reg_completion_cb()
+ * function whenever it called from ST driver.
+ */
+ hst->reg_status = -EINPROGRESS;
+
+ err = st_register(&ti_st_proto[i]);
+ if (!err)
+ goto done;
+
+ if (err != -EINPROGRESS) {
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ BT_ERR("st_register failed %d", err);
+ return err;
+ }
+
+ /* ST is busy with either protocol
+ * registration or firmware download.
+ */
+ BT_DBG("waiting for registration "
+ "completion signal from ST");
+ timeleft = wait_for_completion_timeout
+ (&hst->wait_reg_completion,
+ msecs_to_jiffies(BT_REGISTER_TIMEOUT));
+ if (!timeleft) {
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ BT_ERR("Timeout(%d sec),didn't get reg "
+ "completion signal from ST",
+ BT_REGISTER_TIMEOUT / 1000);
+ return -ETIMEDOUT;
+ }
+
+ /* Is ST registration callback
+ * called with ERROR status? */
+ if (hst->reg_status != 0) {
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ BT_ERR("ST registration completed with invalid "
+ "status %d", hst->reg_status);
+ return -EAGAIN;
+ }
+
+done:
+ hst->st_write = ti_st_proto[i].write;
+ if (!hst->st_write) {
+ BT_ERR("undefined ST write function");
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
+ /* Undo registration with ST */
+ err = st_unregister(&ti_st_proto[i]);
+ if (err)
+ BT_ERR("st_unregister() failed with "
+ "error %d", err);
+ hst->st_write = NULL;
+ }
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+/* Close device */
+static int ti_st_close(struct hci_dev *hdev)
+{
+ int err, i;
+ struct ti_st *hst = hdev->driver_data;
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ for (i = MAX_BT_CHNL_IDS-1; i >= 0; i--) {
+ err = st_unregister(&ti_st_proto[i]);
+ if (err)
+ BT_ERR("st_unregister(%d) failed with error %d",
+ ti_st_proto[i].chnl_id, err);
+ }
+
+ hst->st_write = NULL;
+
+ return err;
+}
+
+static int ti_st_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev;
+ struct ti_st *hst;
+ long len;
+
+ hdev = (struct hci_dev *)skb->dev;
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ hst = hdev->driver_data;
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+ BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type,
+ skb->len);
+
+ /* Insert skb to shared transport layer's transmit queue.
+ * Freeing skb memory is taken care in shared transport layer,
+ * so don't free skb memory here.
+ */
+ len = hst->st_write(skb);
+ if (len < 0) {
+ kfree_skb(skb);
+ BT_ERR("ST write failed (%ld)", len);
+ /* Try Again, would only fail if UART has gone bad */
+ return -EAGAIN;
+ }
+
+ /* ST accepted our skb. So, Go ahead and do rest */
+ hdev->stat.byte_tx += len;
+ ti_st_tx_complete(hst, bt_cb(skb)->pkt_type);
+
+ return 0;
+}
+
+static void ti_st_destruct(struct hci_dev *hdev)
+{
+ BT_DBG("%s", hdev->name);
+ /* do nothing here, since platform remove
+ * would free the hdev->driver_data
+ */
+}
+
+static int bt_ti_probe(struct platform_device *pdev)
+{
+ static struct ti_st *hst;
+ struct hci_dev *hdev;
+ int err;
+
+ hst = kzalloc(sizeof(struct ti_st), GFP_KERNEL);
+ if (!hst)
+ return -ENOMEM;
+
+ /* Expose "hciX" device to user space */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ kfree(hst);
+ return -ENOMEM;
+ }
+
+ BT_DBG("hdev %p", hdev);
+
+ hst->hdev = hdev;
+ hdev->bus = HCI_UART;
+ hdev->driver_data = hst;
+ hdev->open = ti_st_open;
+ hdev->close = ti_st_close;
+ hdev->flush = NULL;
+ hdev->send = ti_st_send_frame;
+ hdev->destruct = ti_st_destruct;
+ hdev->owner = THIS_MODULE;
+
+ err = hci_register_dev(hdev);
+ if (err < 0) {
+ BT_ERR("Can't register HCI device error %d", err);
+ kfree(hst);
+ hci_free_dev(hdev);
+ return err;
+ }
+
+ BT_DBG("HCI device registered (hdev %p)", hdev);
+
+ dev_set_drvdata(&pdev->dev, hst);
+ return err;
+}
+
+static int bt_ti_remove(struct platform_device *pdev)
+{
+ struct hci_dev *hdev;
+ struct ti_st *hst = dev_get_drvdata(&pdev->dev);
+
+ if (!hst)
+ return -EFAULT;
+
+ BT_DBG("%s", hst->hdev->name);
+
+ hdev = hst->hdev;
+ ti_st_close(hdev);
+ hci_unregister_dev(hdev);
+
+ hci_free_dev(hdev);
+ kfree(hst);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+ return 0;
+}
+
+static struct platform_driver btwilink_driver = {
+ .probe = bt_ti_probe,
+ .remove = bt_ti_remove,
+ .driver = {
+ .name = "btwilink",
+ .owner = THIS_MODULE,
+ },
+};
+
+/* ------- Module Init/Exit interfaces ------ */
+static int __init btwilink_init(void)
+{
+ BT_INFO("Bluetooth Driver for TI WiLink - Version %s", VERSION);
+
+ return platform_driver_register(&btwilink_driver);
+}
+
+static void __exit btwilink_exit(void)
+{
+ platform_driver_unregister(&btwilink_driver);
+}
+
+module_init(btwilink_init);
+module_exit(btwilink_exit);
+
+/* ------ Module Info ------ */
+
+MODULE_AUTHOR("Raja Mani <raja_mani@ti.com>");
+MODULE_DESCRIPTION("Bluetooth Driver for TI Shared Transport" VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c
new file mode 100644
index 0000000..7f8045e
--- /dev/null
+++ b/drivers/bluetooth/dtl1_cs.c
@@ -0,0 +1,748 @@
+/*
+ *
+ * A driver for Nokia Connectivity Card DTL-1 devices
+ *
+ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/bitops.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth driver for Nokia Connectivity Card DTL-1");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct dtl1_info_t {
+ struct pcmcia_device *p_dev;
+
+ struct hci_dev *hdev;
+
+ spinlock_t lock; /* For serializing operations */
+
+ unsigned long flowmask; /* HCI flow mask */
+ int ri_latch;
+
+ struct sk_buff_head txq;
+ unsigned long tx_state;
+
+ unsigned long rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+} dtl1_info_t;
+
+
+static int dtl1_config(struct pcmcia_device *link);
+static void dtl1_release(struct pcmcia_device *link);
+
+static void dtl1_detach(struct pcmcia_device *p_dev);
+
+
+/* Transmit states */
+#define XMIT_SENDING 1
+#define XMIT_WAKEUP 2
+#define XMIT_WAITING 8
+
+/* Receiver States */
+#define RECV_WAIT_NSH 0
+#define RECV_WAIT_DATA 1
+
+
+typedef struct {
+ u8 type;
+ u8 zero;
+ u16 len;
+} __packed nsh_t; /* Nokia Specific Header */
+
+#define NSHL 4 /* Nokia Specific Header Length */
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
+{
+ int actual = 0;
+
+ /* Tx FIFO should be empty */
+ if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
+ return 0;
+
+ /* Fill FIFO with current frame */
+ while ((fifo_size-- > 0) && (actual < len)) {
+ /* Transmit next byte */
+ outb(buf[actual], iobase + UART_TX);
+ actual++;
+ }
+
+ return actual;
+}
+
+
+static void dtl1_write_wakeup(dtl1_info_t *info)
+{
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+ if (test_bit(XMIT_WAITING, &(info->tx_state))) {
+ set_bit(XMIT_WAKEUP, &(info->tx_state));
+ return;
+ }
+
+ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
+ set_bit(XMIT_WAKEUP, &(info->tx_state));
+ return;
+ }
+
+ do {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ register unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+ register unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+ register struct sk_buff *skb;
+ register int len;
+
+ clear_bit(XMIT_WAKEUP, &(info->tx_state));
+
+ if (!pcmcia_dev_present(info->p_dev))
+ return;
+
+ if (!(skb = skb_dequeue(&(info->txq))))
+ break;
+
+ /* Send frame */
+ len = dtl1_write(iobase, 32, skb->data, skb->len);
+
+ if (len == skb->len) {
+ set_bit(XMIT_WAITING, &(info->tx_state));
+ kfree_skb(skb);
+ } else {
+ skb_pull(skb, len);
+ skb_queue_head(&(info->txq), skb);
+ }
+
+ info->hdev->stat.byte_tx += len;
+
+ } while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
+
+ clear_bit(XMIT_SENDING, &(info->tx_state));
+}
+
+
+static void dtl1_control(dtl1_info_t *info, struct sk_buff *skb)
+{
+ u8 flowmask = *(u8 *)skb->data;
+ int i;
+
+ printk(KERN_INFO "Bluetooth: Nokia control data =");
+ for (i = 0; i < skb->len; i++) {
+ printk(" %02x", skb->data[i]);
+ }
+ printk("\n");
+
+ /* transition to active state */
+ if (((info->flowmask & 0x07) == 0) && ((flowmask & 0x07) != 0)) {
+ clear_bit(XMIT_WAITING, &(info->tx_state));
+ dtl1_write_wakeup(info);
+ }
+
+ info->flowmask = flowmask;
+
+ kfree_skb(skb);
+}
+
+
+static void dtl1_receive(dtl1_info_t *info)
+{
+ unsigned int iobase;
+ nsh_t *nsh;
+ int boguscount = 0;
+
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ iobase = info->p_dev->resource[0]->start;
+#else
+ iobase = info->p_dev->io.BasePort1;
+#endif
+
+ do {
+ info->hdev->stat.byte_rx++;
+
+ /* Allocate packet */
+ if (info->rx_skb == NULL)
+ if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate mem for new packet");
+ info->rx_state = RECV_WAIT_NSH;
+ info->rx_count = NSHL;
+ return;
+ }
+
+ *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
+ nsh = (nsh_t *)info->rx_skb->data;
+
+ info->rx_count--;
+
+ if (info->rx_count == 0) {
+
+ switch (info->rx_state) {
+ case RECV_WAIT_NSH:
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = nsh->len + (nsh->len & 0x0001);
+ break;
+ case RECV_WAIT_DATA:
+ bt_cb(info->rx_skb)->pkt_type = nsh->type;
+
+ /* remove PAD byte if it exists */
+ if (nsh->len & 0x0001) {
+ info->rx_skb->tail--;
+ info->rx_skb->len--;
+ }
+
+ /* remove NSH */
+ skb_pull(info->rx_skb, NSHL);
+
+ switch (bt_cb(info->rx_skb)->pkt_type) {
+ case 0x80:
+ /* control data for the Nokia Card */
+ dtl1_control(info, info->rx_skb);
+ break;
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ /* send frame to the HCI layer */
+ info->rx_skb->dev = (void *) info->hdev;
+ bt_cb(info->rx_skb)->pkt_type &= 0x0f;
+ hci_recv_frame(info->rx_skb);
+ break;
+ default:
+ /* unknown packet */
+ BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+ kfree_skb(info->rx_skb);
+ break;
+ }
+
+ info->rx_state = RECV_WAIT_NSH;
+ info->rx_count = NSHL;
+ info->rx_skb = NULL;
+ break;
+ }
+
+ }
+
+ /* Make sure we don't stay here too long */
+ if (boguscount++ > 32)
+ break;
+
+ } while (inb(iobase + UART_LSR) & UART_LSR_DR);
+}
+
+
+static irqreturn_t dtl1_interrupt(int irq, void *dev_inst)
+{
+ dtl1_info_t *info = dev_inst;
+ unsigned int iobase;
+ unsigned char msr;
+ int boguscount = 0;
+ int iir, lsr;
+ irqreturn_t r = IRQ_NONE;
+
+ if (!info || !info->hdev)
+ /* our irq handler is shared */
+ return IRQ_NONE;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ iobase = info->p_dev->resource[0]->start;
+#else
+ iobase = info->p_dev->io.BasePort1;
+#endif
+
+ spin_lock(&(info->lock));
+
+ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+ while (iir) {
+
+ r = IRQ_HANDLED;
+ /* Clear interrupt */
+ lsr = inb(iobase + UART_LSR);
+
+ switch (iir) {
+ case UART_IIR_RLSI:
+ BT_ERR("RLSI");
+ break;
+ case UART_IIR_RDI:
+ /* Receive interrupt */
+ dtl1_receive(info);
+ break;
+ case UART_IIR_THRI:
+ if (lsr & UART_LSR_THRE) {
+ /* Transmitter ready for data */
+ dtl1_write_wakeup(info);
+ }
+ break;
+ default:
+ BT_ERR("Unhandled IIR=%#x", iir);
+ break;
+ }
+
+ /* Make sure we don't stay here too long */
+ if (boguscount++ > 100)
+ break;
+
+ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+
+ }
+
+ msr = inb(iobase + UART_MSR);
+
+ if (info->ri_latch ^ (msr & UART_MSR_RI)) {
+ info->ri_latch = msr & UART_MSR_RI;
+ clear_bit(XMIT_WAITING, &(info->tx_state));
+ dtl1_write_wakeup(info);
+ r = IRQ_HANDLED;
+ }
+
+ spin_unlock(&(info->lock));
+
+ return r;
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int dtl1_hci_open(struct hci_dev *hdev)
+{
+ set_bit(HCI_RUNNING, &(hdev->flags));
+
+ return 0;
+}
+
+
+static int dtl1_hci_flush(struct hci_dev *hdev)
+{
+ dtl1_info_t *info = (dtl1_info_t *)(hdev->driver_data);
+
+ /* Drop TX queue */
+ skb_queue_purge(&(info->txq));
+
+ return 0;
+}
+
+
+static int dtl1_hci_close(struct hci_dev *hdev)
+{
+ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+ return 0;
+
+ dtl1_hci_flush(hdev);
+
+ return 0;
+}
+
+
+static int dtl1_hci_send_frame(struct sk_buff *skb)
+{
+ dtl1_info_t *info;
+ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+ struct sk_buff *s;
+ nsh_t nsh;
+
+ if (!hdev) {
+ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+ info = (dtl1_info_t *)(hdev->driver_data);
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ nsh.type = 0x81;
+ break;
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ nsh.type = 0x82;
+ break;
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ nsh.type = 0x83;
+ break;
+ default:
+ return -EILSEQ;
+ };
+
+ nsh.zero = 0;
+ nsh.len = skb->len;
+
+ s = bt_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC);
+ if (!s)
+ return -ENOMEM;
+
+ skb_reserve(s, NSHL);
+ skb_copy_from_linear_data(skb, skb_put(s, skb->len), skb->len);
+ if (skb->len & 0x0001)
+ *skb_put(s, 1) = 0; /* PAD */
+
+ /* Prepend skb with Nokia frame header and queue */
+ memcpy(skb_push(s, NSHL), &nsh, NSHL);
+ skb_queue_tail(&(info->txq), s);
+
+ dtl1_write_wakeup(info);
+
+ kfree_skb(skb);
+
+ return 0;
+}
+
+
+static void dtl1_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+static int dtl1_open(dtl1_info_t *info)
+{
+ unsigned long flags;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+ unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+ struct hci_dev *hdev;
+
+ spin_lock_init(&(info->lock));
+
+ skb_queue_head_init(&(info->txq));
+
+ info->rx_state = RECV_WAIT_NSH;
+ info->rx_count = NSHL;
+ info->rx_skb = NULL;
+
+ set_bit(XMIT_WAITING, &(info->tx_state));
+
+ /* Initialize HCI device */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BT_ERR("Can't allocate HCI device");
+ return -ENOMEM;
+ }
+
+ info->hdev = hdev;
+
+ hdev->bus = HCI_PCCARD;
+ hdev->driver_data = info;
+ SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
+
+ hdev->open = dtl1_hci_open;
+ hdev->close = dtl1_hci_close;
+ hdev->flush = dtl1_hci_flush;
+ hdev->send = dtl1_hci_send_frame;
+ hdev->destruct = dtl1_hci_destruct;
+ hdev->ioctl = dtl1_hci_ioctl;
+
+ hdev->owner = THIS_MODULE;
+
+ spin_lock_irqsave(&(info->lock), flags);
+
+ /* Reset UART */
+ outb(0, iobase + UART_MCR);
+
+ /* Turn off interrupts */
+ outb(0, iobase + UART_IER);
+
+ /* Initialize UART */
+ outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */
+ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ info->ri_latch = inb(info->p_dev->resource[0]->start + UART_MSR)
+ & UART_MSR_RI;
+#else
+ info->ri_latch = inb(info->p_dev->io.BasePort1 + UART_MSR)
+ & UART_MSR_RI;
+#endif
+
+ /* Turn on interrupts */
+ outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
+
+ spin_unlock_irqrestore(&(info->lock), flags);
+
+ /* Timeout before it is safe to send the first HCI packet */
+ msleep(2000);
+
+ /* Register HCI device */
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ info->hdev = NULL;
+ hci_free_dev(hdev);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+
+static int dtl1_close(dtl1_info_t *info)
+{
+ unsigned long flags;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+ unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+ struct hci_dev *hdev = info->hdev;
+
+ if (!hdev)
+ return -ENODEV;
+
+ dtl1_hci_close(hdev);
+
+ spin_lock_irqsave(&(info->lock), flags);
+
+ /* Reset UART */
+ outb(0, iobase + UART_MCR);
+
+ /* Turn off interrupts */
+ outb(0, iobase + UART_IER);
+
+ spin_unlock_irqrestore(&(info->lock), flags);
+
+ hci_unregister_dev(hdev);
+ hci_free_dev(hdev);
+
+ return 0;
+}
+
+static int dtl1_probe(struct pcmcia_device *link)
+{
+ dtl1_info_t *info;
+
+ /* Create new info device */
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->p_dev = link;
+ link->priv = info;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37))
+ link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+#else
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+ link->resource[0]->end = 8;
+#else
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.NumPorts1= 8;
+#endif
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35))
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+ link->irq.Handler = dtl1_interrupt;
+#endif
+
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+#endif
+
+ return dtl1_config(link);
+}
+
+
+static void dtl1_detach(struct pcmcia_device *link)
+{
+ dtl1_info_t *info = link->priv;
+
+ dtl1_release(link);
+
+ kfree(info);
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37))
+static int dtl1_confcheck(struct pcmcia_device *p_dev, void *priv_data)
+{
+ if ((p_dev->resource[1]->end) || (p_dev->resource[1]->end < 8))
+ return -ENODEV;
+
+ p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+ p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+
+ return pcmcia_request_io(p_dev);
+}
+#else
+static int dtl1_confcheck(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if ((cf->io.nwin != 1) || (cf->io.win[0].len <= 8))
+ return -ENODEV;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ p_dev->resource[0]->start = cf->io.win[0].base;
+ p_dev->resource[0]->end = cf->io.win[0].len; /*yo */
+ p_dev->io_lines = cf->io.flags & CISTPL_IO_LINES_MASK;
+ return pcmcia_request_io(p_dev);
+#else
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.NumPorts1 = cf->io.win[0].len; /*yo */
+ p_dev->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
+ return pcmcia_request_io(p_dev, &p_dev->io);
+#endif
+}
+#endif
+
+static int dtl1_config(struct pcmcia_device *link)
+{
+ dtl1_info_t *info = link->priv;
+ int i;
+
+ /* Look for a generic full-sized window */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+ link->resource[0]->end = 8;
+#else
+ link->io.NumPorts1 = 8;
+#endif
+ if (pcmcia_loop_config(link, dtl1_confcheck, NULL) < 0)
+ goto failed;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ i = pcmcia_request_irq(link, dtl1_interrupt);
+ if (i != 0)
+ goto failed;
+#else
+ i = pcmcia_request_irq(link, &link->irq);
+ if (i != 0)
+ link->irq.AssignedIRQ = 0;
+#endif
+
+ i = pcmcia_enable_device(link);
+ if (i != 0)
+ goto failed;
+
+ if (dtl1_open(info) != 0)
+ goto failed;
+
+ return 0;
+
+failed:
+ dtl1_release(link);
+ return -ENODEV;
+}
+
+
+static void dtl1_release(struct pcmcia_device *link)
+{
+ dtl1_info_t *info = link->priv;
+
+ dtl1_close(info);
+
+ pcmcia_disable_device(link);
+}
+
+
+static const struct pcmcia_device_id dtl1_ids[] = {
+ PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-1", 0xe1bfdd64, 0xe168480d),
+ PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-4", 0xe1bfdd64, 0x9102bc82),
+ PCMCIA_DEVICE_PROD_ID12("Socket", "CF", 0xb38bcc2e, 0x44ebf863),
+ PCMCIA_DEVICE_PROD_ID12("Socket", "CF+ Personal Network Card", 0xb38bcc2e, 0xe732bae3),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, dtl1_ids);
+
+static struct pcmcia_driver dtl1_driver = {
+ .owner = THIS_MODULE,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37))
+ .name = "dtl1_cs",
+#else
+ .drv = {
+ .name = "dtl1_cs",
+ },
+#endif
+ .probe = dtl1_probe,
+ .remove = dtl1_detach,
+ .id_table = dtl1_ids,
+};
+
+static int __init init_dtl1_cs(void)
+{
+ return pcmcia_register_driver(&dtl1_driver);
+}
+
+
+static void __exit exit_dtl1_cs(void)
+{
+ pcmcia_unregister_driver(&dtl1_driver);
+}
+
+module_init(init_dtl1_cs);
+module_exit(exit_dtl1_cs);
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
new file mode 100644
index 0000000..4093935
--- /dev/null
+++ b/drivers/bluetooth/hci_ath.c
@@ -0,0 +1,240 @@
+/*
+ * Atheros Communication Bluetooth HCIATH3K UART protocol
+ *
+ * HCIATH3K (HCI Atheros AR300x Protocol) is a Atheros Communication's
+ * power management protocol extension to H4 to support AR300x Bluetooth Chip.
+ *
+ * Copyright (c) 2009-2010 Atheros Communications Inc.
+ *
+ * Acknowledgements:
+ * This file is based on hci_h4.c, which was written
+ * by Maxim Krasnyansky and Marcel Holtmann.
+ *
+ * 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
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+struct ath_struct {
+ struct hci_uart *hu;
+ unsigned int cur_sleep;
+
+ struct sk_buff_head txq;
+ struct work_struct ctxtsw;
+};
+
+static int ath_wakeup_ar3k(struct tty_struct *tty)
+{
+ struct ktermios ktermios;
+ int status = tty->driver->ops->tiocmget(tty);
+
+ if (status & TIOCM_CTS)
+ return status;
+
+ /* Disable Automatic RTSCTS */
+ memcpy(&ktermios, tty->termios, sizeof(ktermios));
+ ktermios.c_cflag &= ~CRTSCTS;
+ tty_set_termios(tty, &ktermios);
+
+ /* Clear RTS first */
+ status = tty->driver->ops->tiocmget(tty);
+ tty->driver->ops->tiocmset(tty, 0x00, TIOCM_RTS);
+ mdelay(20);
+
+ /* Set RTS, wake up board */
+ status = tty->driver->ops->tiocmget(tty);
+ tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00);
+ mdelay(20);
+
+ status = tty->driver->ops->tiocmget(tty);
+
+ /* Disable Automatic RTSCTS */
+ ktermios.c_cflag |= CRTSCTS;
+ status = tty_set_termios(tty, &ktermios);
+
+ return status;
+}
+
+static void ath_hci_uart_work(struct work_struct *work)
+{
+ int status;
+ struct ath_struct *ath;
+ struct hci_uart *hu;
+ struct tty_struct *tty;
+
+ ath = container_of(work, struct ath_struct, ctxtsw);
+
+ hu = ath->hu;
+ tty = hu->tty;
+
+ /* verify and wake up controller */
+ if (ath->cur_sleep) {
+ status = ath_wakeup_ar3k(tty);
+ if (!(status & TIOCM_CTS))
+ return;
+ }
+
+ /* Ready to send Data */
+ clear_bit(HCI_UART_SENDING, &hu->tx_state);
+ hci_uart_tx_wakeup(hu);
+}
+
+/* Initialize protocol */
+static int ath_open(struct hci_uart *hu)
+{
+ struct ath_struct *ath;
+
+ BT_DBG("hu %p", hu);
+
+ ath = kzalloc(sizeof(*ath), GFP_ATOMIC);
+ if (!ath)
+ return -ENOMEM;
+
+ skb_queue_head_init(&ath->txq);
+
+ hu->priv = ath;
+ ath->hu = hu;
+
+ INIT_WORK(&ath->ctxtsw, ath_hci_uart_work);
+
+ return 0;
+}
+
+/* Flush protocol data */
+static int ath_flush(struct hci_uart *hu)
+{
+ struct ath_struct *ath = hu->priv;
+
+ BT_DBG("hu %p", hu);
+
+ skb_queue_purge(&ath->txq);
+
+ return 0;
+}
+
+/* Close protocol */
+static int ath_close(struct hci_uart *hu)
+{
+ struct ath_struct *ath = hu->priv;
+
+ BT_DBG("hu %p", hu);
+
+ skb_queue_purge(&ath->txq);
+
+ cancel_work_sync(&ath->ctxtsw);
+
+ hu->priv = NULL;
+ kfree(ath);
+
+ return 0;
+}
+
+#define HCI_OP_ATH_SLEEP 0xFC04
+
+/* Enqueue frame for transmittion */
+static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+ struct ath_struct *ath = hu->priv;
+
+ if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /*
+ * Update power management enable flag with parameters of
+ * HCI sleep enable vendor specific HCI command.
+ */
+ if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
+ struct hci_command_hdr *hdr = (void *)skb->data;
+
+ if (__le16_to_cpu(hdr->opcode) == HCI_OP_ATH_SLEEP)
+ ath->cur_sleep = skb->data[HCI_COMMAND_HDR_SIZE];
+ }
+
+ BT_DBG("hu %p skb %p", hu, skb);
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+ skb_queue_tail(&ath->txq, skb);
+ set_bit(HCI_UART_SENDING, &hu->tx_state);
+
+ schedule_work(&ath->ctxtsw);
+
+ return 0;
+}
+
+static struct sk_buff *ath_dequeue(struct hci_uart *hu)
+{
+ struct ath_struct *ath = hu->priv;
+
+ return skb_dequeue(&ath->txq);
+}
+
+/* Recv data */
+static int ath_recv(struct hci_uart *hu, void *data, int count)
+{
+ int ret;
+
+ ret = hci_recv_stream_fragment(hu->hdev, data, count);
+ if (ret < 0) {
+ BT_ERR("Frame Reassembly Failed");
+ return ret;
+ }
+
+ return count;
+}
+
+static struct hci_uart_proto athp = {
+ .id = HCI_UART_ATH3K,
+ .open = ath_open,
+ .close = ath_close,
+ .recv = ath_recv,
+ .enqueue = ath_enqueue,
+ .dequeue = ath_dequeue,
+ .flush = ath_flush,
+};
+
+int __init ath_init(void)
+{
+ int err = hci_uart_register_proto(&athp);
+
+ if (!err)
+ BT_INFO("HCIATH3K protocol initialized");
+ else
+ BT_ERR("HCIATH3K protocol registration failed");
+
+ return err;
+}
+
+int __exit ath_deinit(void)
+{
+ return hci_uart_unregister_proto(&athp);
+}
diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c
new file mode 100644
index 0000000..9c5b2dc
--- /dev/null
+++ b/drivers/bluetooth/hci_bcsp.c
@@ -0,0 +1,763 @@
+/*
+ *
+ * Bluetooth HCI UART driver
+ *
+ * Copyright (C) 2002-2003 Fabrizio Gennari <fabrizio.gennari@philips.com>
+ * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+#include <linux/bitrev.h>
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+#define VERSION "0.3"
+
+static int txcrc = 1;
+static int hciextn = 1;
+
+#define BCSP_TXWINSIZE 4
+
+#define BCSP_ACK_PKT 0x05
+#define BCSP_LE_PKT 0x06
+
+struct bcsp_struct {
+ struct sk_buff_head unack; /* Unack'ed packets queue */
+ struct sk_buff_head rel; /* Reliable packets queue */
+ struct sk_buff_head unrel; /* Unreliable packets queue */
+
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+ u8 rxseq_txack; /* rxseq == txack. */
+ u8 rxack; /* Last packet sent by us that the peer ack'ed */
+ struct timer_list tbcsp;
+
+ enum {
+ BCSP_W4_PKT_DELIMITER,
+ BCSP_W4_PKT_START,
+ BCSP_W4_BCSP_HDR,
+ BCSP_W4_DATA,
+ BCSP_W4_CRC
+ } rx_state;
+
+ enum {
+ BCSP_ESCSTATE_NOESC,
+ BCSP_ESCSTATE_ESC
+ } rx_esc_state;
+
+ u8 use_crc;
+ u16 message_crc;
+ u8 txack_req; /* Do we need to send ack's to the peer? */
+
+ /* Reliable packet sequence number - used to assign seq to each rel pkt. */
+ u8 msgq_txseq;
+};
+
+/* ---- BCSP CRC calculation ---- */
+
+/* Table for calculating CRC for polynomial 0x1021, LSB processed first,
+initial value 0xffff, bits shifted in reverse order. */
+
+static const u16 crc_table[] = {
+ 0x0000, 0x1081, 0x2102, 0x3183,
+ 0x4204, 0x5285, 0x6306, 0x7387,
+ 0x8408, 0x9489, 0xa50a, 0xb58b,
+ 0xc60c, 0xd68d, 0xe70e, 0xf78f
+};
+
+/* Initialise the crc calculator */
+#define BCSP_CRC_INIT(x) x = 0xffff
+
+/*
+ Update crc with next data byte
+
+ Implementation note
+ The data byte is treated as two nibbles. The crc is generated
+ in reverse, i.e., bits are fed into the register from the top.
+*/
+static void bcsp_crc_update(u16 *crc, u8 d)
+{
+ u16 reg = *crc;
+
+ reg = (reg >> 4) ^ crc_table[(reg ^ d) & 0x000f];
+ reg = (reg >> 4) ^ crc_table[(reg ^ (d >> 4)) & 0x000f];
+
+ *crc = reg;
+}
+
+/* ---- BCSP core ---- */
+
+static void bcsp_slip_msgdelim(struct sk_buff *skb)
+{
+ const char pkt_delim = 0xc0;
+
+ memcpy(skb_put(skb, 1), &pkt_delim, 1);
+}
+
+static void bcsp_slip_one_byte(struct sk_buff *skb, u8 c)
+{
+ const char esc_c0[2] = { 0xdb, 0xdc };
+ const char esc_db[2] = { 0xdb, 0xdd };
+
+ switch (c) {
+ case 0xc0:
+ memcpy(skb_put(skb, 2), &esc_c0, 2);
+ break;
+ case 0xdb:
+ memcpy(skb_put(skb, 2), &esc_db, 2);
+ break;
+ default:
+ memcpy(skb_put(skb, 1), &c, 1);
+ }
+}
+
+static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+ struct bcsp_struct *bcsp = hu->priv;
+
+ if (skb->len > 0xFFF) {
+ BT_ERR("Packet too long");
+ kfree_skb(skb);
+ return 0;
+ }
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_ACLDATA_PKT:
+ case HCI_COMMAND_PKT:
+ skb_queue_tail(&bcsp->rel, skb);
+ break;
+
+ case HCI_SCODATA_PKT:
+ skb_queue_tail(&bcsp->unrel, skb);
+ break;
+
+ default:
+ BT_ERR("Unknown packet type");
+ kfree_skb(skb);
+ break;
+ }
+
+ return 0;
+}
+
+static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
+ int len, int pkt_type)
+{
+ struct sk_buff *nskb;
+ u8 hdr[4], chan;
+ u16 BCSP_CRC_INIT(bcsp_txmsg_crc);
+ int rel, i;
+
+ switch (pkt_type) {
+ case HCI_ACLDATA_PKT:
+ chan = 6; /* BCSP ACL channel */
+ rel = 1; /* reliable channel */
+ break;
+ case HCI_COMMAND_PKT:
+ chan = 5; /* BCSP cmd/evt channel */
+ rel = 1; /* reliable channel */
+ break;
+ case HCI_SCODATA_PKT:
+ chan = 7; /* BCSP SCO channel */
+ rel = 0; /* unreliable channel */
+ break;
+ case BCSP_LE_PKT:
+ chan = 1; /* BCSP LE channel */
+ rel = 0; /* unreliable channel */
+ break;
+ case BCSP_ACK_PKT:
+ chan = 0; /* BCSP internal channel */
+ rel = 0; /* unreliable channel */
+ break;
+ default:
+ BT_ERR("Unknown packet type");
+ return NULL;
+ }
+
+ if (hciextn && chan == 5) {
+ __le16 opcode = ((struct hci_command_hdr *)data)->opcode;
+
+ /* Vendor specific commands */
+ if (hci_opcode_ogf(__le16_to_cpu(opcode)) == 0x3f) {
+ u8 desc = *(data + HCI_COMMAND_HDR_SIZE);
+ if ((desc & 0xf0) == 0xc0) {
+ data += HCI_COMMAND_HDR_SIZE + 1;
+ len -= HCI_COMMAND_HDR_SIZE + 1;
+ chan = desc & 0x0f;
+ }
+ }
+ }
+
+ /* Max len of packet: (original len +4(bcsp hdr) +2(crc))*2
+ (because bytes 0xc0 and 0xdb are escaped, worst case is
+ when the packet is all made of 0xc0 and 0xdb :) )
+ + 2 (0xc0 delimiters at start and end). */
+
+ nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC);
+ if (!nskb)
+ return NULL;
+
+ bt_cb(nskb)->pkt_type = pkt_type;
+
+ bcsp_slip_msgdelim(nskb);
+
+ hdr[0] = bcsp->rxseq_txack << 3;
+ bcsp->txack_req = 0;
+ BT_DBG("We request packet no %u to card", bcsp->rxseq_txack);
+
+ if (rel) {
+ hdr[0] |= 0x80 + bcsp->msgq_txseq;
+ BT_DBG("Sending packet with seqno %u", bcsp->msgq_txseq);
+ bcsp->msgq_txseq = (bcsp->msgq_txseq + 1) & 0x07;
+ }
+
+ if (bcsp->use_crc)
+ hdr[0] |= 0x40;
+
+ hdr[1] = ((len << 4) & 0xff) | chan;
+ hdr[2] = len >> 4;
+ hdr[3] = ~(hdr[0] + hdr[1] + hdr[2]);
+
+ /* Put BCSP header */
+ for (i = 0; i < 4; i++) {
+ bcsp_slip_one_byte(nskb, hdr[i]);
+
+ if (bcsp->use_crc)
+ bcsp_crc_update(&bcsp_txmsg_crc, hdr[i]);
+ }
+
+ /* Put payload */
+ for (i = 0; i < len; i++) {
+ bcsp_slip_one_byte(nskb, data[i]);
+
+ if (bcsp->use_crc)
+ bcsp_crc_update(&bcsp_txmsg_crc, data[i]);
+ }
+
+ /* Put CRC */
+ if (bcsp->use_crc) {
+ bcsp_txmsg_crc = bitrev16(bcsp_txmsg_crc);
+ bcsp_slip_one_byte(nskb, (u8) ((bcsp_txmsg_crc >> 8) & 0x00ff));
+ bcsp_slip_one_byte(nskb, (u8) (bcsp_txmsg_crc & 0x00ff));
+ }
+
+ bcsp_slip_msgdelim(nskb);
+ return nskb;
+}
+
+/* This is a rewrite of pkt_avail in ABCSP */
+static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
+{
+ struct bcsp_struct *bcsp = hu->priv;
+ unsigned long flags;
+ struct sk_buff *skb;
+
+ /* First of all, check for unreliable messages in the queue,
+ since they have priority */
+
+ if ((skb = skb_dequeue(&bcsp->unrel)) != NULL) {
+ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, bt_cb(skb)->pkt_type);
+ if (nskb) {
+ kfree_skb(skb);
+ return nskb;
+ } else {
+ skb_queue_head(&bcsp->unrel, skb);
+ BT_ERR("Could not dequeue pkt because alloc_skb failed");
+ }
+ }
+
+ /* Now, try to send a reliable pkt. We can only send a
+ reliable packet if the number of packets sent but not yet ack'ed
+ is < than the winsize */
+
+ spin_lock_irqsave_nested(&bcsp->unack.lock, flags, SINGLE_DEPTH_NESTING);
+
+ if (bcsp->unack.qlen < BCSP_TXWINSIZE && (skb = skb_dequeue(&bcsp->rel)) != NULL) {
+ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, bt_cb(skb)->pkt_type);
+ if (nskb) {
+ __skb_queue_tail(&bcsp->unack, skb);
+ mod_timer(&bcsp->tbcsp, jiffies + HZ / 4);
+ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+ return nskb;
+ } else {
+ skb_queue_head(&bcsp->rel, skb);
+ BT_ERR("Could not dequeue pkt because alloc_skb failed");
+ }
+ }
+
+ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+
+ /* We could not send a reliable packet, either because there are
+ none or because there are too many unack'ed pkts. Did we receive
+ any packets we have not acknowledged yet ? */
+
+ if (bcsp->txack_req) {
+ /* if so, craft an empty ACK pkt and send it on BCSP unreliable
+ channel 0 */
+ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, NULL, 0, BCSP_ACK_PKT);
+ return nskb;
+ }
+
+ /* We have nothing to send */
+ return NULL;
+}
+
+static int bcsp_flush(struct hci_uart *hu)
+{
+ BT_DBG("hu %p", hu);
+ return 0;
+}
+
+/* Remove ack'ed packets */
+static void bcsp_pkt_cull(struct bcsp_struct *bcsp)
+{
+ struct sk_buff *skb, *tmp;
+ unsigned long flags;
+ int i, pkts_to_be_removed;
+ u8 seqno;
+
+ spin_lock_irqsave(&bcsp->unack.lock, flags);
+
+ pkts_to_be_removed = skb_queue_len(&bcsp->unack);
+ seqno = bcsp->msgq_txseq;
+
+ while (pkts_to_be_removed) {
+ if (bcsp->rxack == seqno)
+ break;
+ pkts_to_be_removed--;
+ seqno = (seqno - 1) & 0x07;
+ }
+
+ if (bcsp->rxack != seqno)
+ BT_ERR("Peer acked invalid packet");
+
+ BT_DBG("Removing %u pkts out of %u, up to seqno %u",
+ pkts_to_be_removed, skb_queue_len(&bcsp->unack),
+ (seqno - 1) & 0x07);
+
+ i = 0;
+ skb_queue_walk_safe(&bcsp->unack, skb, tmp) {
+ if (i >= pkts_to_be_removed)
+ break;
+ i++;
+
+ __skb_unlink(skb, &bcsp->unack);
+ kfree_skb(skb);
+ }
+
+ if (skb_queue_empty(&bcsp->unack))
+ del_timer(&bcsp->tbcsp);
+
+ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+
+ if (i != pkts_to_be_removed)
+ BT_ERR("Removed only %u out of %u pkts", i, pkts_to_be_removed);
+}
+
+/* Handle BCSP link-establishment packets. When we
+ detect a "sync" packet, symptom that the BT module has reset,
+ we do nothing :) (yet) */
+static void bcsp_handle_le_pkt(struct hci_uart *hu)
+{
+ struct bcsp_struct *bcsp = hu->priv;
+ u8 conf_pkt[4] = { 0xad, 0xef, 0xac, 0xed };
+ u8 conf_rsp_pkt[4] = { 0xde, 0xad, 0xd0, 0xd0 };
+ u8 sync_pkt[4] = { 0xda, 0xdc, 0xed, 0xed };
+
+ /* spot "conf" pkts and reply with a "conf rsp" pkt */
+ if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
+ !memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) {
+ struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC);
+
+ BT_DBG("Found a LE conf pkt");
+ if (!nskb)
+ return;
+ memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4);
+ bt_cb(nskb)->pkt_type = BCSP_LE_PKT;
+
+ skb_queue_head(&bcsp->unrel, nskb);
+ hci_uart_tx_wakeup(hu);
+ }
+ /* Spot "sync" pkts. If we find one...disaster! */
+ else if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
+ !memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) {
+ BT_ERR("Found a LE sync pkt, card has reset");
+ }
+}
+
+static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char byte)
+{
+ const u8 c0 = 0xc0, db = 0xdb;
+
+ switch (bcsp->rx_esc_state) {
+ case BCSP_ESCSTATE_NOESC:
+ switch (byte) {
+ case 0xdb:
+ bcsp->rx_esc_state = BCSP_ESCSTATE_ESC;
+ break;
+ default:
+ memcpy(skb_put(bcsp->rx_skb, 1), &byte, 1);
+ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
+ bcsp->rx_state != BCSP_W4_CRC)
+ bcsp_crc_update(&bcsp->message_crc, byte);
+ bcsp->rx_count--;
+ }
+ break;
+
+ case BCSP_ESCSTATE_ESC:
+ switch (byte) {
+ case 0xdc:
+ memcpy(skb_put(bcsp->rx_skb, 1), &c0, 1);
+ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
+ bcsp->rx_state != BCSP_W4_CRC)
+ bcsp_crc_update(&bcsp-> message_crc, 0xc0);
+ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
+ bcsp->rx_count--;
+ break;
+
+ case 0xdd:
+ memcpy(skb_put(bcsp->rx_skb, 1), &db, 1);
+ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
+ bcsp->rx_state != BCSP_W4_CRC)
+ bcsp_crc_update(&bcsp-> message_crc, 0xdb);
+ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
+ bcsp->rx_count--;
+ break;
+
+ default:
+ BT_ERR ("Invalid byte %02x after esc byte", byte);
+ kfree_skb(bcsp->rx_skb);
+ bcsp->rx_skb = NULL;
+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+ bcsp->rx_count = 0;
+ }
+ }
+}
+
+static void bcsp_complete_rx_pkt(struct hci_uart *hu)
+{
+ struct bcsp_struct *bcsp = hu->priv;
+ int pass_up;
+
+ if (bcsp->rx_skb->data[0] & 0x80) { /* reliable pkt */
+ BT_DBG("Received seqno %u from card", bcsp->rxseq_txack);
+ bcsp->rxseq_txack++;
+ bcsp->rxseq_txack %= 0x8;
+ bcsp->txack_req = 1;
+
+ /* If needed, transmit an ack pkt */
+ hci_uart_tx_wakeup(hu);
+ }
+
+ bcsp->rxack = (bcsp->rx_skb->data[0] >> 3) & 0x07;
+ BT_DBG("Request for pkt %u from card", bcsp->rxack);
+
+ bcsp_pkt_cull(bcsp);
+ if ((bcsp->rx_skb->data[1] & 0x0f) == 6 &&
+ bcsp->rx_skb->data[0] & 0x80) {
+ bt_cb(bcsp->rx_skb)->pkt_type = HCI_ACLDATA_PKT;
+ pass_up = 1;
+ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 &&
+ bcsp->rx_skb->data[0] & 0x80) {
+ bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT;
+ pass_up = 1;
+ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) {
+ bt_cb(bcsp->rx_skb)->pkt_type = HCI_SCODATA_PKT;
+ pass_up = 1;
+ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 &&
+ !(bcsp->rx_skb->data[0] & 0x80)) {
+ bcsp_handle_le_pkt(hu);
+ pass_up = 0;
+ } else
+ pass_up = 0;
+
+ if (!pass_up) {
+ struct hci_event_hdr hdr;
+ u8 desc = (bcsp->rx_skb->data[1] & 0x0f);
+
+ if (desc != 0 && desc != 1) {
+ if (hciextn) {
+ desc |= 0xc0;
+ skb_pull(bcsp->rx_skb, 4);
+ memcpy(skb_push(bcsp->rx_skb, 1), &desc, 1);
+
+ hdr.evt = 0xff;
+ hdr.plen = bcsp->rx_skb->len;
+ memcpy(skb_push(bcsp->rx_skb, HCI_EVENT_HDR_SIZE), &hdr, HCI_EVENT_HDR_SIZE);
+ bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT;
+
+ hci_recv_frame(bcsp->rx_skb);
+ } else {
+ BT_ERR ("Packet for unknown channel (%u %s)",
+ bcsp->rx_skb->data[1] & 0x0f,
+ bcsp->rx_skb->data[0] & 0x80 ?
+ "reliable" : "unreliable");
+ kfree_skb(bcsp->rx_skb);
+ }
+ } else
+ kfree_skb(bcsp->rx_skb);
+ } else {
+ /* Pull out BCSP hdr */
+ skb_pull(bcsp->rx_skb, 4);
+
+ hci_recv_frame(bcsp->rx_skb);
+ }
+
+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+ bcsp->rx_skb = NULL;
+}
+
+static u16 bscp_get_crc(struct bcsp_struct *bcsp)
+{
+ return get_unaligned_be16(&bcsp->rx_skb->data[bcsp->rx_skb->len - 2]);
+}
+
+/* Recv data */
+static int bcsp_recv(struct hci_uart *hu, void *data, int count)
+{
+ struct bcsp_struct *bcsp = hu->priv;
+ register unsigned char *ptr;
+
+ BT_DBG("hu %p count %d rx_state %d rx_count %ld",
+ hu, count, bcsp->rx_state, bcsp->rx_count);
+
+ ptr = data;
+ while (count) {
+ if (bcsp->rx_count) {
+ if (*ptr == 0xc0) {
+ BT_ERR("Short BCSP packet");
+ kfree_skb(bcsp->rx_skb);
+ bcsp->rx_state = BCSP_W4_PKT_START;
+ bcsp->rx_count = 0;
+ } else
+ bcsp_unslip_one_byte(bcsp, *ptr);
+
+ ptr++; count--;
+ continue;
+ }
+
+ switch (bcsp->rx_state) {
+ case BCSP_W4_BCSP_HDR:
+ if ((0xff & (u8) ~ (bcsp->rx_skb->data[0] + bcsp->rx_skb->data[1] +
+ bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) {
+ BT_ERR("Error in BCSP hdr checksum");
+ kfree_skb(bcsp->rx_skb);
+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+ bcsp->rx_count = 0;
+ continue;
+ }
+ if (bcsp->rx_skb->data[0] & 0x80 /* reliable pkt */
+ && (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) {
+ BT_ERR ("Out-of-order packet arrived, got %u expected %u",
+ bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack);
+
+ kfree_skb(bcsp->rx_skb);
+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+ bcsp->rx_count = 0;
+ continue;
+ }
+ bcsp->rx_state = BCSP_W4_DATA;
+ bcsp->rx_count = (bcsp->rx_skb->data[1] >> 4) +
+ (bcsp->rx_skb->data[2] << 4); /* May be 0 */
+ continue;
+
+ case BCSP_W4_DATA:
+ if (bcsp->rx_skb->data[0] & 0x40) { /* pkt with crc */
+ bcsp->rx_state = BCSP_W4_CRC;
+ bcsp->rx_count = 2;
+ } else
+ bcsp_complete_rx_pkt(hu);
+ continue;
+
+ case BCSP_W4_CRC:
+ if (bitrev16(bcsp->message_crc) != bscp_get_crc(bcsp)) {
+ BT_ERR ("Checksum failed: computed %04x received %04x",
+ bitrev16(bcsp->message_crc),
+ bscp_get_crc(bcsp));
+
+ kfree_skb(bcsp->rx_skb);
+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+ bcsp->rx_count = 0;
+ continue;
+ }
+ skb_trim(bcsp->rx_skb, bcsp->rx_skb->len - 2);
+ bcsp_complete_rx_pkt(hu);
+ continue;
+
+ case BCSP_W4_PKT_DELIMITER:
+ switch (*ptr) {
+ case 0xc0:
+ bcsp->rx_state = BCSP_W4_PKT_START;
+ break;
+ default:
+ /*BT_ERR("Ignoring byte %02x", *ptr);*/
+ break;
+ }
+ ptr++; count--;
+ break;
+
+ case BCSP_W4_PKT_START:
+ switch (*ptr) {
+ case 0xc0:
+ ptr++; count--;
+ break;
+
+ default:
+ bcsp->rx_state = BCSP_W4_BCSP_HDR;
+ bcsp->rx_count = 4;
+ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
+ BCSP_CRC_INIT(bcsp->message_crc);
+
+ /* Do not increment ptr or decrement count
+ * Allocate packet. Max len of a BCSP pkt=
+ * 0xFFF (payload) +4 (header) +2 (crc) */
+
+ bcsp->rx_skb = bt_skb_alloc(0x1005, GFP_ATOMIC);
+ if (!bcsp->rx_skb) {
+ BT_ERR("Can't allocate mem for new packet");
+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+ bcsp->rx_count = 0;
+ return 0;
+ }
+ bcsp->rx_skb->dev = (void *) hu->hdev;
+ break;
+ }
+ break;
+ }
+ }
+ return count;
+}
+
+ /* Arrange to retransmit all messages in the relq. */
+static void bcsp_timed_event(unsigned long arg)
+{
+ struct hci_uart *hu = (struct hci_uart *) arg;
+ struct bcsp_struct *bcsp = hu->priv;
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ BT_DBG("hu %p retransmitting %u pkts", hu, bcsp->unack.qlen);
+
+ spin_lock_irqsave_nested(&bcsp->unack.lock, flags, SINGLE_DEPTH_NESTING);
+
+ while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) {
+ bcsp->msgq_txseq = (bcsp->msgq_txseq - 1) & 0x07;
+ skb_queue_head(&bcsp->rel, skb);
+ }
+
+ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+
+ hci_uart_tx_wakeup(hu);
+}
+
+static int bcsp_open(struct hci_uart *hu)
+{
+ struct bcsp_struct *bcsp;
+
+ BT_DBG("hu %p", hu);
+
+ bcsp = kzalloc(sizeof(*bcsp), GFP_ATOMIC);
+ if (!bcsp)
+ return -ENOMEM;
+
+ hu->priv = bcsp;
+ skb_queue_head_init(&bcsp->unack);
+ skb_queue_head_init(&bcsp->rel);
+ skb_queue_head_init(&bcsp->unrel);
+
+ init_timer(&bcsp->tbcsp);
+ bcsp->tbcsp.function = bcsp_timed_event;
+ bcsp->tbcsp.data = (u_long) hu;
+
+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+
+ if (txcrc)
+ bcsp->use_crc = 1;
+
+ return 0;
+}
+
+static int bcsp_close(struct hci_uart *hu)
+{
+ struct bcsp_struct *bcsp = hu->priv;
+ hu->priv = NULL;
+
+ BT_DBG("hu %p", hu);
+
+ skb_queue_purge(&bcsp->unack);
+ skb_queue_purge(&bcsp->rel);
+ skb_queue_purge(&bcsp->unrel);
+ del_timer(&bcsp->tbcsp);
+
+ kfree(bcsp);
+ return 0;
+}
+
+static struct hci_uart_proto bcsp = {
+ .id = HCI_UART_BCSP,
+ .open = bcsp_open,
+ .close = bcsp_close,
+ .enqueue = bcsp_enqueue,
+ .dequeue = bcsp_dequeue,
+ .recv = bcsp_recv,
+ .flush = bcsp_flush
+};
+
+int __init bcsp_init(void)
+{
+ int err = hci_uart_register_proto(&bcsp);
+
+ if (!err)
+ BT_INFO("HCI BCSP protocol initialized");
+ else
+ BT_ERR("HCI BCSP protocol registration failed");
+
+ return err;
+}
+
+int __exit bcsp_deinit(void)
+{
+ return hci_uart_unregister_proto(&bcsp);
+}
+
+module_param(txcrc, bool, 0644);
+MODULE_PARM_DESC(txcrc, "Transmit CRC with every BCSP packet");
+
+module_param(hciextn, bool, 0644);
+MODULE_PARM_DESC(hciextn, "Convert HCI Extensions into BCSP packets");
diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
new file mode 100644
index 0000000..2fcd8b3
--- /dev/null
+++ b/drivers/bluetooth/hci_h4.c
@@ -0,0 +1,196 @@
+/*
+ *
+ * Bluetooth HCI UART driver
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+#define VERSION "1.2"
+
+struct h4_struct {
+ unsigned long rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+ struct sk_buff_head txq;
+};
+
+/* H4 receiver States */
+#define H4_W4_PACKET_TYPE 0
+#define H4_W4_EVENT_HDR 1
+#define H4_W4_ACL_HDR 2
+#define H4_W4_SCO_HDR 3
+#define H4_W4_DATA 4
+
+/* Initialize protocol */
+static int h4_open(struct hci_uart *hu)
+{
+ struct h4_struct *h4;
+
+ BT_DBG("hu %p", hu);
+
+ h4 = kzalloc(sizeof(*h4), GFP_ATOMIC);
+ if (!h4)
+ return -ENOMEM;
+
+ skb_queue_head_init(&h4->txq);
+
+ hu->priv = h4;
+ return 0;
+}
+
+/* Flush protocol data */
+static int h4_flush(struct hci_uart *hu)
+{
+ struct h4_struct *h4 = hu->priv;
+
+ BT_DBG("hu %p", hu);
+
+ skb_queue_purge(&h4->txq);
+
+ return 0;
+}
+
+/* Close protocol */
+static int h4_close(struct hci_uart *hu)
+{
+ struct h4_struct *h4 = hu->priv;
+
+ hu->priv = NULL;
+
+ BT_DBG("hu %p", hu);
+
+ skb_queue_purge(&h4->txq);
+
+ kfree_skb(h4->rx_skb);
+
+ hu->priv = NULL;
+ kfree(h4);
+
+ return 0;
+}
+
+/* Enqueue frame for transmittion (padding, crc, etc) */
+static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+ struct h4_struct *h4 = hu->priv;
+
+ BT_DBG("hu %p skb %p", hu, skb);
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ skb_queue_tail(&h4->txq, skb);
+
+ return 0;
+}
+
+static inline int h4_check_data_len(struct h4_struct *h4, int len)
+{
+ register int room = skb_tailroom(h4->rx_skb);
+
+ BT_DBG("len %d room %d", len, room);
+
+ if (!len) {
+ hci_recv_frame(h4->rx_skb);
+ } else if (len > room) {
+ BT_ERR("Data length is too large");
+ kfree_skb(h4->rx_skb);
+ } else {
+ h4->rx_state = H4_W4_DATA;
+ h4->rx_count = len;
+ return len;
+ }
+
+ h4->rx_state = H4_W4_PACKET_TYPE;
+ h4->rx_skb = NULL;
+ h4->rx_count = 0;
+
+ return 0;
+}
+
+/* Recv data */
+static int h4_recv(struct hci_uart *hu, void *data, int count)
+{
+ int ret;
+
+ ret = hci_recv_stream_fragment(hu->hdev, data, count);
+ if (ret < 0) {
+ BT_ERR("Frame Reassembly Failed");
+ return ret;
+ }
+
+ return count;
+}
+
+static struct sk_buff *h4_dequeue(struct hci_uart *hu)
+{
+ struct h4_struct *h4 = hu->priv;
+ return skb_dequeue(&h4->txq);
+}
+
+static struct hci_uart_proto h4p = {
+ .id = HCI_UART_H4,
+ .open = h4_open,
+ .close = h4_close,
+ .recv = h4_recv,
+ .enqueue = h4_enqueue,
+ .dequeue = h4_dequeue,
+ .flush = h4_flush,
+};
+
+int __init h4_init(void)
+{
+ int err = hci_uart_register_proto(&h4p);
+
+ if (!err)
+ BT_INFO("HCI H4 protocol initialized");
+ else
+ BT_ERR("HCI H4 protocol registration failed");
+
+ return err;
+}
+
+int __exit h4_deinit(void)
+{
+ return hci_uart_unregister_proto(&h4p);
+}
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
new file mode 100644
index 0000000..ca6804d
--- /dev/null
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -0,0 +1,615 @@
+/*
+ *
+ * Bluetooth HCI UART driver
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+#define VERSION "2.2"
+
+static int reset = 0;
+
+static struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
+
+int hci_uart_register_proto(struct hci_uart_proto *p)
+{
+ if (p->id >= HCI_UART_MAX_PROTO)
+ return -EINVAL;
+
+ if (hup[p->id])
+ return -EEXIST;
+
+ hup[p->id] = p;
+
+ return 0;
+}
+
+int hci_uart_unregister_proto(struct hci_uart_proto *p)
+{
+ if (p->id >= HCI_UART_MAX_PROTO)
+ return -EINVAL;
+
+ if (!hup[p->id])
+ return -EINVAL;
+
+ hup[p->id] = NULL;
+
+ return 0;
+}
+
+static struct hci_uart_proto *hci_uart_get_proto(unsigned int id)
+{
+ if (id >= HCI_UART_MAX_PROTO)
+ return NULL;
+
+ return hup[id];
+}
+
+static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type)
+{
+ struct hci_dev *hdev = hu->hdev;
+
+ /* Update HCI stat counters */
+ switch (pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+ }
+}
+
+static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu)
+{
+ struct sk_buff *skb = hu->tx_skb;
+
+ if (!skb)
+ skb = hu->proto->dequeue(hu);
+ else
+ hu->tx_skb = NULL;
+
+ return skb;
+}
+
+int hci_uart_tx_wakeup(struct hci_uart *hu)
+{
+ struct tty_struct *tty = hu->tty;
+ struct hci_dev *hdev = hu->hdev;
+ struct sk_buff *skb;
+
+ if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) {
+ set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
+ return 0;
+ }
+
+ BT_DBG("");
+
+restart:
+ clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
+
+ while ((skb = hci_uart_dequeue(hu))) {
+ int len;
+
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+ len = tty->ops->write(tty, skb->data, skb->len);
+ hdev->stat.byte_tx += len;
+
+ skb_pull(skb, len);
+ if (skb->len) {
+ hu->tx_skb = skb;
+ break;
+ }
+
+ hci_uart_tx_complete(hu, bt_cb(skb)->pkt_type);
+ kfree_skb(skb);
+ }
+
+ if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state))
+ goto restart;
+
+ clear_bit(HCI_UART_SENDING, &hu->tx_state);
+ return 0;
+}
+
+/* ------- Interface to HCI layer ------ */
+/* Initialize device */
+static int hci_uart_open(struct hci_dev *hdev)
+{
+ BT_DBG("%s %p", hdev->name, hdev);
+
+ /* Nothing to do for UART driver */
+
+ set_bit(HCI_RUNNING, &hdev->flags);
+
+ return 0;
+}
+
+/* Reset device */
+static int hci_uart_flush(struct hci_dev *hdev)
+{
+ struct hci_uart *hu = (struct hci_uart *) hdev->driver_data;
+ struct tty_struct *tty = hu->tty;
+
+ BT_DBG("hdev %p tty %p", hdev, tty);
+
+ if (hu->tx_skb) {
+ kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
+ }
+
+ /* Flush any pending characters in the driver and discipline. */
+ tty_ldisc_flush(tty);
+ tty_driver_flush_buffer(tty);
+
+ if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+ hu->proto->flush(hu);
+
+ return 0;
+}
+
+/* Close device */
+static int hci_uart_close(struct hci_dev *hdev)
+{
+ BT_DBG("hdev %p", hdev);
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ hci_uart_flush(hdev);
+ hdev->flush = NULL;
+ return 0;
+}
+
+/* Send frames from HCI layer */
+static int hci_uart_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev* hdev = (struct hci_dev *) skb->dev;
+ struct hci_uart *hu;
+
+ if (!hdev) {
+ BT_ERR("Frame for unknown device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ hu = (struct hci_uart *) hdev->driver_data;
+
+ BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
+
+ hu->proto->enqueue(hu, skb);
+
+ hci_uart_tx_wakeup(hu);
+
+ return 0;
+}
+
+static void hci_uart_destruct(struct hci_dev *hdev)
+{
+ if (!hdev)
+ return;
+
+ BT_DBG("%s", hdev->name);
+ kfree(hdev->driver_data);
+}
+
+/* ------ LDISC part ------ */
+/* hci_uart_tty_open
+ *
+ * Called when line discipline changed to HCI_UART.
+ *
+ * Arguments:
+ * tty pointer to tty info structure
+ * Return Value:
+ * 0 if success, otherwise error code
+ */
+static int hci_uart_tty_open(struct tty_struct *tty)
+{
+ struct hci_uart *hu = (void *) tty->disc_data;
+
+ BT_DBG("tty %p", tty);
+
+ /* FIXME: This btw is bogus, nothing requires the old ldisc to clear
+ the pointer */
+ if (hu)
+ return -EEXIST;
+
+ /* Error if the tty has no write op instead of leaving an exploitable
+ hole */
+ if (tty->ops->write == NULL)
+ return -EOPNOTSUPP;
+
+ if (!(hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL))) {
+ BT_ERR("Can't allocate control structure");
+ return -ENFILE;
+ }
+
+ tty->disc_data = hu;
+ hu->tty = tty;
+ tty->receive_room = 65536;
+
+ spin_lock_init(&hu->rx_lock);
+
+ /* Flush any pending characters in the driver and line discipline. */
+
+ /* FIXME: why is this needed. Note don't use ldisc_ref here as the
+ open path is before the ldisc is referencable */
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+ if (tty->ldisc->ops->flush_buffer)
+ tty->ldisc->ops->flush_buffer(tty);
+#else
+ if (tty->ldisc.ops->flush_buffer)
+ tty->ldisc.ops->flush_buffer(tty);
+#endif
+ tty_driver_flush_buffer(tty);
+
+ return 0;
+}
+
+/* hci_uart_tty_close()
+ *
+ * Called when the line discipline is changed to something
+ * else, the tty is closed, or the tty detects a hangup.
+ */
+static void hci_uart_tty_close(struct tty_struct *tty)
+{
+ struct hci_uart *hu = (void *)tty->disc_data;
+
+ BT_DBG("tty %p", tty);
+
+ /* Detach from the tty */
+ tty->disc_data = NULL;
+
+ if (hu) {
+ struct hci_dev *hdev = hu->hdev;
+
+ if (hdev)
+ hci_uart_close(hdev);
+
+ if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) {
+ hu->proto->close(hu);
+ if (hdev) {
+ hci_unregister_dev(hdev);
+ hci_free_dev(hdev);
+ }
+ }
+ }
+}
+
+/* hci_uart_tty_wakeup()
+ *
+ * Callback for transmit wakeup. Called when low level
+ * device driver can accept more send data.
+ *
+ * Arguments: tty pointer to associated tty instance data
+ * Return Value: None
+ */
+static void hci_uart_tty_wakeup(struct tty_struct *tty)
+{
+ struct hci_uart *hu = (void *)tty->disc_data;
+
+ BT_DBG("");
+
+ if (!hu)
+ return;
+
+ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+
+ if (tty != hu->tty)
+ return;
+
+ if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+ hci_uart_tx_wakeup(hu);
+}
+
+/* hci_uart_tty_receive()
+ *
+ * Called by tty low level driver when receive data is
+ * available.
+ *
+ * Arguments: tty pointer to tty isntance data
+ * data pointer to received data
+ * flags pointer to flags for data
+ * count count of received data in bytes
+ *
+ * Return Value: None
+ */
+static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count)
+{
+ struct hci_uart *hu = (void *)tty->disc_data;
+
+ if (!hu || tty != hu->tty)
+ return;
+
+ if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
+ return;
+
+ spin_lock(&hu->rx_lock);
+ hu->proto->recv(hu, (void *) data, count);
+ hu->hdev->stat.byte_rx += count;
+ spin_unlock(&hu->rx_lock);
+
+ tty_unthrottle(tty);
+}
+
+static int hci_uart_register_dev(struct hci_uart *hu)
+{
+ struct hci_dev *hdev;
+
+ BT_DBG("");
+
+ /* Initialize and register HCI device */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BT_ERR("Can't allocate HCI device");
+ return -ENOMEM;
+ }
+
+ hu->hdev = hdev;
+
+ hdev->bus = HCI_UART;
+ hdev->driver_data = hu;
+
+ hdev->open = hci_uart_open;
+ hdev->close = hci_uart_close;
+ hdev->flush = hci_uart_flush;
+ hdev->send = hci_uart_send_frame;
+ hdev->destruct = hci_uart_destruct;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)
+ hdev->parent = hu->tty->dev;
+#endif
+
+ hdev->owner = THIS_MODULE;
+
+ if (!reset)
+ set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
+
+ if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags))
+ set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
+
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ hci_free_dev(hdev);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int hci_uart_set_proto(struct hci_uart *hu, int id)
+{
+ struct hci_uart_proto *p;
+ int err;
+
+ p = hci_uart_get_proto(id);
+ if (!p)
+ return -EPROTONOSUPPORT;
+
+ err = p->open(hu);
+ if (err)
+ return err;
+
+ hu->proto = p;
+
+ err = hci_uart_register_dev(hu);
+ if (err) {
+ p->close(hu);
+ return err;
+ }
+
+ return 0;
+}
+
+/* hci_uart_tty_ioctl()
+ *
+ * Process IOCTL system call for the tty device.
+ *
+ * Arguments:
+ *
+ * tty pointer to tty instance data
+ * file pointer to open file object for device
+ * cmd IOCTL command code
+ * arg argument for IOCTL call (cmd dependent)
+ *
+ * Return Value: Command dependent
+ */
+static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct hci_uart *hu = (void *)tty->disc_data;
+ int err = 0;
+
+ BT_DBG("");
+
+ /* Verify the status of the device */
+ if (!hu)
+ return -EBADF;
+
+ switch (cmd) {
+ case HCIUARTSETPROTO:
+ if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
+ err = hci_uart_set_proto(hu, arg);
+ if (err) {
+ clear_bit(HCI_UART_PROTO_SET, &hu->flags);
+ return err;
+ }
+ } else
+ return -EBUSY;
+ break;
+
+ case HCIUARTGETPROTO:
+ if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+ return hu->proto->id;
+ return -EUNATCH;
+
+ case HCIUARTGETDEVICE:
+ if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+ return hu->hdev->id;
+ return -EUNATCH;
+
+ case HCIUARTSETFLAGS:
+ if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+ return -EBUSY;
+ hu->hdev_flags = arg;
+ break;
+
+ case HCIUARTGETFLAGS:
+ return hu->hdev_flags;
+
+ default:
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27))
+ err = n_tty_ioctl_helper(tty, file, cmd, arg);
+#else
+ err = n_tty_ioctl(tty, file, cmd, arg);
+#endif
+ break;
+ };
+
+ return err;
+}
+
+/*
+ * We don't provide read/write/poll interface for user space.
+ */
+static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file,
+ unsigned char __user *buf, size_t nr)
+{
+ return 0;
+}
+
+static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file,
+ const unsigned char *data, size_t count)
+{
+ return 0;
+}
+
+static unsigned int hci_uart_tty_poll(struct tty_struct *tty,
+ struct file *filp, poll_table *wait)
+{
+ return 0;
+}
+
+static int __init hci_uart_init(void)
+{
+ static struct tty_ldisc_ops hci_uart_ldisc;
+ int err;
+
+ BT_INFO("HCI UART driver ver %s", VERSION);
+
+ /* Register the tty discipline */
+
+ memset(&hci_uart_ldisc, 0, sizeof (hci_uart_ldisc));
+ hci_uart_ldisc.magic = TTY_LDISC_MAGIC;
+ hci_uart_ldisc.name = "n_hci";
+ hci_uart_ldisc.open = hci_uart_tty_open;
+ hci_uart_ldisc.close = hci_uart_tty_close;
+ hci_uart_ldisc.read = hci_uart_tty_read;
+ hci_uart_ldisc.write = hci_uart_tty_write;
+ hci_uart_ldisc.ioctl = hci_uart_tty_ioctl;
+ hci_uart_ldisc.poll = hci_uart_tty_poll;
+ hci_uart_ldisc.receive_buf = hci_uart_tty_receive;
+ hci_uart_ldisc.write_wakeup = hci_uart_tty_wakeup;
+ hci_uart_ldisc.owner = THIS_MODULE;
+
+ if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) {
+ BT_ERR("HCI line discipline registration failed. (%d)", err);
+ return err;
+ }
+
+#ifdef CONFIG_BT_HCIUART_H4
+ h4_init();
+#endif
+#ifdef CONFIG_BT_HCIUART_BCSP
+ bcsp_init();
+#endif
+#ifdef CONFIG_BT_HCIUART_LL
+ ll_init();
+#endif
+#ifdef CONFIG_BT_HCIUART_ATH3K
+ ath_init();
+#endif
+
+ return 0;
+}
+
+static void __exit hci_uart_exit(void)
+{
+ int err;
+
+#ifdef CONFIG_BT_HCIUART_H4
+ h4_deinit();
+#endif
+#ifdef CONFIG_BT_HCIUART_BCSP
+ bcsp_deinit();
+#endif
+#ifdef CONFIG_BT_HCIUART_LL
+ ll_deinit();
+#endif
+#ifdef CONFIG_BT_HCIUART_ATH3K
+ ath_deinit();
+#endif
+
+ /* Release tty registration of line discipline */
+ if ((err = tty_unregister_ldisc(N_HCI)))
+ BT_ERR("Can't unregister HCI line discipline (%d)", err);
+}
+
+module_init(hci_uart_init);
+module_exit(hci_uart_exit);
+
+module_param(reset, bool, 0644);
+MODULE_PARM_DESC(reset, "Send HCI reset command on initialization");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth HCI UART driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_LDISC(N_HCI);
diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
new file mode 100644
index 0000000..7e4b435
--- /dev/null
+++ b/drivers/bluetooth/hci_ll.c
@@ -0,0 +1,535 @@
+/*
+ * Texas Instruments' Bluetooth HCILL UART protocol
+ *
+ * HCILL (HCI Low Level) is a Texas Instruments' power management
+ * protocol extension to H4.
+ *
+ * Copyright (C) 2007 Texas Instruments, Inc.
+ *
+ * Written by Ohad Ben-Cohen <ohad@bencohen.org>
+ *
+ * Acknowledgements:
+ * This file is based on hci_h4.c, which was written
+ * by Maxim Krasnyansky and Marcel Holtmann.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+/* HCILL commands */
+#define HCILL_GO_TO_SLEEP_IND 0x30
+#define HCILL_GO_TO_SLEEP_ACK 0x31
+#define HCILL_WAKE_UP_IND 0x32
+#define HCILL_WAKE_UP_ACK 0x33
+
+/* HCILL receiver States */
+#define HCILL_W4_PACKET_TYPE 0
+#define HCILL_W4_EVENT_HDR 1
+#define HCILL_W4_ACL_HDR 2
+#define HCILL_W4_SCO_HDR 3
+#define HCILL_W4_DATA 4
+
+/* HCILL states */
+enum hcill_states_e {
+ HCILL_ASLEEP,
+ HCILL_ASLEEP_TO_AWAKE,
+ HCILL_AWAKE,
+ HCILL_AWAKE_TO_ASLEEP
+};
+
+struct hcill_cmd {
+ u8 cmd;
+} __packed;
+
+struct ll_struct {
+ unsigned long rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+ struct sk_buff_head txq;
+ spinlock_t hcill_lock; /* HCILL state lock */
+ unsigned long hcill_state; /* HCILL power state */
+ struct sk_buff_head tx_wait_q; /* HCILL wait queue */
+};
+
+/*
+ * Builds and sends an HCILL command packet.
+ * These are very simple packets with only 1 cmd byte
+ */
+static int send_hcill_cmd(u8 cmd, struct hci_uart *hu)
+{
+ int err = 0;
+ struct sk_buff *skb = NULL;
+ struct ll_struct *ll = hu->priv;
+ struct hcill_cmd *hcill_packet;
+
+ BT_DBG("hu %p cmd 0x%x", hu, cmd);
+
+ /* allocate packet */
+ skb = bt_skb_alloc(1, GFP_ATOMIC);
+ if (!skb) {
+ BT_ERR("cannot allocate memory for HCILL packet");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* prepare packet */
+ hcill_packet = (struct hcill_cmd *) skb_put(skb, 1);
+ hcill_packet->cmd = cmd;
+ skb->dev = (void *) hu->hdev;
+
+ /* send packet */
+ skb_queue_tail(&ll->txq, skb);
+out:
+ return err;
+}
+
+/* Initialize protocol */
+static int ll_open(struct hci_uart *hu)
+{
+ struct ll_struct *ll;
+
+ BT_DBG("hu %p", hu);
+
+ ll = kzalloc(sizeof(*ll), GFP_ATOMIC);
+ if (!ll)
+ return -ENOMEM;
+
+ skb_queue_head_init(&ll->txq);
+ skb_queue_head_init(&ll->tx_wait_q);
+ spin_lock_init(&ll->hcill_lock);
+
+ ll->hcill_state = HCILL_AWAKE;
+
+ hu->priv = ll;
+
+ return 0;
+}
+
+/* Flush protocol data */
+static int ll_flush(struct hci_uart *hu)
+{
+ struct ll_struct *ll = hu->priv;
+
+ BT_DBG("hu %p", hu);
+
+ skb_queue_purge(&ll->tx_wait_q);
+ skb_queue_purge(&ll->txq);
+
+ return 0;
+}
+
+/* Close protocol */
+static int ll_close(struct hci_uart *hu)
+{
+ struct ll_struct *ll = hu->priv;
+
+ BT_DBG("hu %p", hu);
+
+ skb_queue_purge(&ll->tx_wait_q);
+ skb_queue_purge(&ll->txq);
+
+ kfree_skb(ll->rx_skb);
+
+ hu->priv = NULL;
+
+ kfree(ll);
+
+ return 0;
+}
+
+/*
+ * internal function, which does common work of the device wake up process:
+ * 1. places all pending packets (waiting in tx_wait_q list) in txq list.
+ * 2. changes internal state to HCILL_AWAKE.
+ * Note: assumes that hcill_lock spinlock is taken,
+ * shouldn't be called otherwise!
+ */
+static void __ll_do_awake(struct ll_struct *ll)
+{
+ struct sk_buff *skb = NULL;
+
+ while ((skb = skb_dequeue(&ll->tx_wait_q)))
+ skb_queue_tail(&ll->txq, skb);
+
+ ll->hcill_state = HCILL_AWAKE;
+}
+
+/*
+ * Called upon a wake-up-indication from the device
+ */
+static void ll_device_want_to_wakeup(struct hci_uart *hu)
+{
+ unsigned long flags;
+ struct ll_struct *ll = hu->priv;
+
+ BT_DBG("hu %p", hu);
+
+ /* lock hcill state */
+ spin_lock_irqsave(&ll->hcill_lock, flags);
+
+ switch (ll->hcill_state) {
+ case HCILL_ASLEEP_TO_AWAKE:
+ /*
+ * This state means that both the host and the BRF chip
+ * have simultaneously sent a wake-up-indication packet.
+ * Traditionally, in this case, receiving a wake-up-indication
+ * was enough and an additional wake-up-ack wasn't needed.
+ * This has changed with the BRF6350, which does require an
+ * explicit wake-up-ack. Other BRF versions, which do not
+ * require an explicit ack here, do accept it, thus it is
+ * perfectly safe to always send one.
+ */
+ BT_DBG("dual wake-up-indication");
+ /* deliberate fall-through - do not add break */
+ case HCILL_ASLEEP:
+ /* acknowledge device wake up */
+ if (send_hcill_cmd(HCILL_WAKE_UP_ACK, hu) < 0) {
+ BT_ERR("cannot acknowledge device wake up");
+ goto out;
+ }
+ break;
+ default:
+ /* any other state is illegal */
+ BT_ERR("received HCILL_WAKE_UP_IND in state %ld", ll->hcill_state);
+ break;
+ }
+
+ /* send pending packets and change state to HCILL_AWAKE */
+ __ll_do_awake(ll);
+
+out:
+ spin_unlock_irqrestore(&ll->hcill_lock, flags);
+
+ /* actually send the packets */
+ hci_uart_tx_wakeup(hu);
+}
+
+/*
+ * Called upon a sleep-indication from the device
+ */
+static void ll_device_want_to_sleep(struct hci_uart *hu)
+{
+ unsigned long flags;
+ struct ll_struct *ll = hu->priv;
+
+ BT_DBG("hu %p", hu);
+
+ /* lock hcill state */
+ spin_lock_irqsave(&ll->hcill_lock, flags);
+
+ /* sanity check */
+ if (ll->hcill_state != HCILL_AWAKE)
+ BT_ERR("ERR: HCILL_GO_TO_SLEEP_IND in state %ld", ll->hcill_state);
+
+ /* acknowledge device sleep */
+ if (send_hcill_cmd(HCILL_GO_TO_SLEEP_ACK, hu) < 0) {
+ BT_ERR("cannot acknowledge device sleep");
+ goto out;
+ }
+
+ /* update state */
+ ll->hcill_state = HCILL_ASLEEP;
+
+out:
+ spin_unlock_irqrestore(&ll->hcill_lock, flags);
+
+ /* actually send the sleep ack packet */
+ hci_uart_tx_wakeup(hu);
+}
+
+/*
+ * Called upon wake-up-acknowledgement from the device
+ */
+static void ll_device_woke_up(struct hci_uart *hu)
+{
+ unsigned long flags;
+ struct ll_struct *ll = hu->priv;
+
+ BT_DBG("hu %p", hu);
+
+ /* lock hcill state */
+ spin_lock_irqsave(&ll->hcill_lock, flags);
+
+ /* sanity check */
+ if (ll->hcill_state != HCILL_ASLEEP_TO_AWAKE)
+ BT_ERR("received HCILL_WAKE_UP_ACK in state %ld", ll->hcill_state);
+
+ /* send pending packets and change state to HCILL_AWAKE */
+ __ll_do_awake(ll);
+
+ spin_unlock_irqrestore(&ll->hcill_lock, flags);
+
+ /* actually send the packets */
+ hci_uart_tx_wakeup(hu);
+}
+
+/* Enqueue frame for transmittion (padding, crc, etc) */
+/* may be called from two simultaneous tasklets */
+static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+ unsigned long flags = 0;
+ struct ll_struct *ll = hu->priv;
+
+ BT_DBG("hu %p skb %p", hu, skb);
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+ /* lock hcill state */
+ spin_lock_irqsave(&ll->hcill_lock, flags);
+
+ /* act according to current state */
+ switch (ll->hcill_state) {
+ case HCILL_AWAKE:
+ BT_DBG("device awake, sending normally");
+ skb_queue_tail(&ll->txq, skb);
+ break;
+ case HCILL_ASLEEP:
+ BT_DBG("device asleep, waking up and queueing packet");
+ /* save packet for later */
+ skb_queue_tail(&ll->tx_wait_q, skb);
+ /* awake device */
+ if (send_hcill_cmd(HCILL_WAKE_UP_IND, hu) < 0) {
+ BT_ERR("cannot wake up device");
+ break;
+ }
+ ll->hcill_state = HCILL_ASLEEP_TO_AWAKE;
+ break;
+ case HCILL_ASLEEP_TO_AWAKE:
+ BT_DBG("device waking up, queueing packet");
+ /* transient state; just keep packet for later */
+ skb_queue_tail(&ll->tx_wait_q, skb);
+ break;
+ default:
+ BT_ERR("illegal hcill state: %ld (losing packet)", ll->hcill_state);
+ kfree_skb(skb);
+ break;
+ }
+
+ spin_unlock_irqrestore(&ll->hcill_lock, flags);
+
+ return 0;
+}
+
+static inline int ll_check_data_len(struct ll_struct *ll, int len)
+{
+ register int room = skb_tailroom(ll->rx_skb);
+
+ BT_DBG("len %d room %d", len, room);
+
+ if (!len) {
+ hci_recv_frame(ll->rx_skb);
+ } else if (len > room) {
+ BT_ERR("Data length is too large");
+ kfree_skb(ll->rx_skb);
+ } else {
+ ll->rx_state = HCILL_W4_DATA;
+ ll->rx_count = len;
+ return len;
+ }
+
+ ll->rx_state = HCILL_W4_PACKET_TYPE;
+ ll->rx_skb = NULL;
+ ll->rx_count = 0;
+
+ return 0;
+}
+
+/* Recv data */
+static int ll_recv(struct hci_uart *hu, void *data, int count)
+{
+ struct ll_struct *ll = hu->priv;
+ register char *ptr;
+ struct hci_event_hdr *eh;
+ struct hci_acl_hdr *ah;
+ struct hci_sco_hdr *sh;
+ register int len, type, dlen;
+
+ BT_DBG("hu %p count %d rx_state %ld rx_count %ld", hu, count, ll->rx_state, ll->rx_count);
+
+ ptr = data;
+ while (count) {
+ if (ll->rx_count) {
+ len = min_t(unsigned int, ll->rx_count, count);
+ memcpy(skb_put(ll->rx_skb, len), ptr, len);
+ ll->rx_count -= len; count -= len; ptr += len;
+
+ if (ll->rx_count)
+ continue;
+
+ switch (ll->rx_state) {
+ case HCILL_W4_DATA:
+ BT_DBG("Complete data");
+ hci_recv_frame(ll->rx_skb);
+
+ ll->rx_state = HCILL_W4_PACKET_TYPE;
+ ll->rx_skb = NULL;
+ continue;
+
+ case HCILL_W4_EVENT_HDR:
+ eh = hci_event_hdr(ll->rx_skb);
+
+ BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
+
+ ll_check_data_len(ll, eh->plen);
+ continue;
+
+ case HCILL_W4_ACL_HDR:
+ ah = hci_acl_hdr(ll->rx_skb);
+ dlen = __le16_to_cpu(ah->dlen);
+
+ BT_DBG("ACL header: dlen %d", dlen);
+
+ ll_check_data_len(ll, dlen);
+ continue;
+
+ case HCILL_W4_SCO_HDR:
+ sh = hci_sco_hdr(ll->rx_skb);
+
+ BT_DBG("SCO header: dlen %d", sh->dlen);
+
+ ll_check_data_len(ll, sh->dlen);
+ continue;
+ }
+ }
+
+ /* HCILL_W4_PACKET_TYPE */
+ switch (*ptr) {
+ case HCI_EVENT_PKT:
+ BT_DBG("Event packet");
+ ll->rx_state = HCILL_W4_EVENT_HDR;
+ ll->rx_count = HCI_EVENT_HDR_SIZE;
+ type = HCI_EVENT_PKT;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ BT_DBG("ACL packet");
+ ll->rx_state = HCILL_W4_ACL_HDR;
+ ll->rx_count = HCI_ACL_HDR_SIZE;
+ type = HCI_ACLDATA_PKT;
+ break;
+
+ case HCI_SCODATA_PKT:
+ BT_DBG("SCO packet");
+ ll->rx_state = HCILL_W4_SCO_HDR;
+ ll->rx_count = HCI_SCO_HDR_SIZE;
+ type = HCI_SCODATA_PKT;
+ break;
+
+ /* HCILL signals */
+ case HCILL_GO_TO_SLEEP_IND:
+ BT_DBG("HCILL_GO_TO_SLEEP_IND packet");
+ ll_device_want_to_sleep(hu);
+ ptr++; count--;
+ continue;
+
+ case HCILL_GO_TO_SLEEP_ACK:
+ /* shouldn't happen */
+ BT_ERR("received HCILL_GO_TO_SLEEP_ACK (in state %ld)", ll->hcill_state);
+ ptr++; count--;
+ continue;
+
+ case HCILL_WAKE_UP_IND:
+ BT_DBG("HCILL_WAKE_UP_IND packet");
+ ll_device_want_to_wakeup(hu);
+ ptr++; count--;
+ continue;
+
+ case HCILL_WAKE_UP_ACK:
+ BT_DBG("HCILL_WAKE_UP_ACK packet");
+ ll_device_woke_up(hu);
+ ptr++; count--;
+ continue;
+
+ default:
+ BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
+ hu->hdev->stat.err_rx++;
+ ptr++; count--;
+ continue;
+ };
+
+ ptr++; count--;
+
+ /* Allocate packet */
+ ll->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+ if (!ll->rx_skb) {
+ BT_ERR("Can't allocate mem for new packet");
+ ll->rx_state = HCILL_W4_PACKET_TYPE;
+ ll->rx_count = 0;
+ return -ENOMEM;
+ }
+
+ ll->rx_skb->dev = (void *) hu->hdev;
+ bt_cb(ll->rx_skb)->pkt_type = type;
+ }
+
+ return count;
+}
+
+static struct sk_buff *ll_dequeue(struct hci_uart *hu)
+{
+ struct ll_struct *ll = hu->priv;
+ return skb_dequeue(&ll->txq);
+}
+
+static struct hci_uart_proto llp = {
+ .id = HCI_UART_LL,
+ .open = ll_open,
+ .close = ll_close,
+ .recv = ll_recv,
+ .enqueue = ll_enqueue,
+ .dequeue = ll_dequeue,
+ .flush = ll_flush,
+};
+
+int __init ll_init(void)
+{
+ int err = hci_uart_register_proto(&llp);
+
+ if (!err)
+ BT_INFO("HCILL protocol initialized");
+ else
+ BT_ERR("HCILL protocol registration failed");
+
+ return err;
+}
+
+int __exit ll_deinit(void)
+{
+ return hci_uart_unregister_proto(&llp);
+}
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
new file mode 100644
index 0000000..99fb352
--- /dev/null
+++ b/drivers/bluetooth/hci_uart.h
@@ -0,0 +1,104 @@
+/*
+ *
+ * Bluetooth HCI UART driver
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ *
+ */
+
+#ifndef N_HCI
+#define N_HCI 15
+#endif
+
+/* Ioctls */
+#define HCIUARTSETPROTO _IOW('U', 200, int)
+#define HCIUARTGETPROTO _IOR('U', 201, int)
+#define HCIUARTGETDEVICE _IOR('U', 202, int)
+#define HCIUARTSETFLAGS _IOW('U', 203, int)
+#define HCIUARTGETFLAGS _IOR('U', 204, int)
+
+/* UART protocols */
+#define HCI_UART_MAX_PROTO 6
+
+#define HCI_UART_H4 0
+#define HCI_UART_BCSP 1
+#define HCI_UART_3WIRE 2
+#define HCI_UART_H4DS 3
+#define HCI_UART_LL 4
+#define HCI_UART_ATH3K 5
+
+#define HCI_UART_RAW_DEVICE 0
+
+struct hci_uart;
+
+struct hci_uart_proto {
+ unsigned int id;
+ int (*open)(struct hci_uart *hu);
+ int (*close)(struct hci_uart *hu);
+ int (*flush)(struct hci_uart *hu);
+ int (*recv)(struct hci_uart *hu, void *data, int len);
+ int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
+ struct sk_buff *(*dequeue)(struct hci_uart *hu);
+};
+
+struct hci_uart {
+ struct tty_struct *tty;
+ struct hci_dev *hdev;
+ unsigned long flags;
+ unsigned long hdev_flags;
+
+ struct hci_uart_proto *proto;
+ void *priv;
+
+ struct sk_buff *tx_skb;
+ unsigned long tx_state;
+ spinlock_t rx_lock;
+};
+
+/* HCI_UART proto flag bits */
+#define HCI_UART_PROTO_SET 0
+
+/* TX states */
+#define HCI_UART_SENDING 1
+#define HCI_UART_TX_WAKEUP 2
+
+int hci_uart_register_proto(struct hci_uart_proto *p);
+int hci_uart_unregister_proto(struct hci_uart_proto *p);
+int hci_uart_tx_wakeup(struct hci_uart *hu);
+
+#ifdef CONFIG_BT_HCIUART_H4
+int h4_init(void);
+int h4_deinit(void);
+#endif
+
+#ifdef CONFIG_BT_HCIUART_BCSP
+int bcsp_init(void);
+int bcsp_deinit(void);
+#endif
+
+#ifdef CONFIG_BT_HCIUART_LL
+int ll_init(void);
+int ll_deinit(void);
+#endif
+
+#ifdef CONFIG_BT_HCIUART_ATH3K
+int ath_init(void);
+int ath_deinit(void);
+#endif
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
new file mode 100644
index 0000000..2e302a1
--- /dev/null
+++ b/drivers/bluetooth/hci_vhci.c
@@ -0,0 +1,309 @@
+/*
+ *
+ * Bluetooth virtual HCI driver
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2004-2006 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+
+#include <linux/skbuff.h>
+#include <linux/miscdevice.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#define VERSION "1.3"
+
+struct vhci_data {
+ struct hci_dev *hdev;
+
+ unsigned long flags;
+
+ wait_queue_head_t read_wait;
+ struct sk_buff_head readq;
+};
+
+static int vhci_open_dev(struct hci_dev *hdev)
+{
+ set_bit(HCI_RUNNING, &hdev->flags);
+
+ return 0;
+}
+
+static int vhci_close_dev(struct hci_dev *hdev)
+{
+ struct vhci_data *data = hdev->driver_data;
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ skb_queue_purge(&data->readq);
+
+ return 0;
+}
+
+static int vhci_flush(struct hci_dev *hdev)
+{
+ struct vhci_data *data = hdev->driver_data;
+
+ skb_queue_purge(&data->readq);
+
+ return 0;
+}
+
+static int vhci_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev* hdev = (struct hci_dev *) skb->dev;
+ struct vhci_data *data;
+
+ if (!hdev) {
+ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ data = hdev->driver_data;
+
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ skb_queue_tail(&data->readq, skb);
+
+ wake_up_interruptible(&data->read_wait);
+
+ return 0;
+}
+
+static void vhci_destruct(struct hci_dev *hdev)
+{
+ kfree(hdev->driver_data);
+}
+
+static inline ssize_t vhci_get_user(struct vhci_data *data,
+ const char __user *buf, size_t count)
+{
+ struct sk_buff *skb;
+
+ if (count > HCI_MAX_FRAME_SIZE)
+ return -EINVAL;
+
+ skb = bt_skb_alloc(count, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ if (copy_from_user(skb_put(skb, count), buf, count)) {
+ kfree_skb(skb);
+ return -EFAULT;
+ }
+
+ skb->dev = (void *) data->hdev;
+ bt_cb(skb)->pkt_type = *((__u8 *) skb->data);
+ skb_pull(skb, 1);
+
+ hci_recv_frame(skb);
+
+ return count;
+}
+
+static inline ssize_t vhci_put_user(struct vhci_data *data,
+ struct sk_buff *skb, char __user *buf, int count)
+{
+ char __user *ptr = buf;
+ int len, total = 0;
+
+ len = min_t(unsigned int, skb->len, count);
+
+ if (copy_to_user(ptr, skb->data, len))
+ return -EFAULT;
+
+ total += len;
+
+ data->hdev->stat.byte_tx += len;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ data->hdev->stat.cmd_tx++;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ data->hdev->stat.acl_tx++;
+ break;
+
+ case HCI_SCODATA_PKT:
+ data->hdev->stat.sco_tx++;
+ break;
+ };
+
+ return total;
+}
+
+static ssize_t vhci_read(struct file *file,
+ char __user *buf, size_t count, loff_t *pos)
+{
+ struct vhci_data *data = file->private_data;
+ struct sk_buff *skb;
+ ssize_t ret = 0;
+
+ while (count) {
+ skb = skb_dequeue(&data->readq);
+ if (skb) {
+ ret = vhci_put_user(data, skb, buf, count);
+ if (ret < 0)
+ skb_queue_head(&data->readq, skb);
+ else
+ kfree_skb(skb);
+ break;
+ }
+
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ ret = wait_event_interruptible(data->read_wait,
+ !skb_queue_empty(&data->readq));
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static ssize_t vhci_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *pos)
+{
+ struct vhci_data *data = file->private_data;
+
+ return vhci_get_user(data, buf, count);
+}
+
+static unsigned int vhci_poll(struct file *file, poll_table *wait)
+{
+ struct vhci_data *data = file->private_data;
+
+ poll_wait(file, &data->read_wait, wait);
+
+ if (!skb_queue_empty(&data->readq))
+ return POLLIN | POLLRDNORM;
+
+ return POLLOUT | POLLWRNORM;
+}
+
+static int vhci_open(struct inode *inode, struct file *file)
+{
+ struct vhci_data *data;
+ struct hci_dev *hdev;
+
+ data = kzalloc(sizeof(struct vhci_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ skb_queue_head_init(&data->readq);
+ init_waitqueue_head(&data->read_wait);
+
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ data->hdev = hdev;
+
+ hdev->bus = HCI_VIRTUAL;
+ hdev->driver_data = data;
+
+ hdev->open = vhci_open_dev;
+ hdev->close = vhci_close_dev;
+ hdev->flush = vhci_flush;
+ hdev->send = vhci_send_frame;
+ hdev->destruct = vhci_destruct;
+
+ hdev->owner = THIS_MODULE;
+
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ kfree(data);
+ hci_free_dev(hdev);
+ return -EBUSY;
+ }
+
+ file->private_data = data;
+
+ return nonseekable_open(inode, file);
+}
+
+static int vhci_release(struct inode *inode, struct file *file)
+{
+ struct vhci_data *data = file->private_data;
+ struct hci_dev *hdev = data->hdev;
+
+ hci_unregister_dev(hdev);
+ hci_free_dev(hdev);
+
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static const struct file_operations vhci_fops = {
+ .owner = THIS_MODULE,
+ .read = vhci_read,
+ .write = vhci_write,
+ .poll = vhci_poll,
+ .open = vhci_open,
+ .release = vhci_release,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice vhci_miscdev= {
+ .name = "vhci",
+ .fops = &vhci_fops,
+ .minor = MISC_DYNAMIC_MINOR,
+};
+
+static int __init vhci_init(void)
+{
+ BT_INFO("Virtual HCI driver ver %s", VERSION);
+
+ return misc_register(&vhci_miscdev);
+}
+
+static void __exit vhci_exit(void)
+{
+ misc_deregister(&vhci_miscdev);
+}
+
+module_init(vhci_init);
+module_exit(vhci_exit);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
new file mode 100644
index 0000000..151feb2
--- /dev/null
+++ b/drivers/misc/eeprom/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
diff --git a/drivers/misc/eeprom/eeprom_93cx6.c b/drivers/misc/eeprom/eeprom_93cx6.c
new file mode 100644
index 0000000..7b33de9
--- /dev/null
+++ b/drivers/misc/eeprom/eeprom_93cx6.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2004 - 2006 rt2x00 SourceForge Project
+ * <http://rt2x00.serialmonkey.com>
+ *
+ * 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.
+ *
+ * Module: eeprom_93cx6
+ * Abstract: EEPROM reader routines for 93cx6 chipsets.
+ * Supported chipsets: 93c46 & 93c66.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/eeprom_93cx6.h>
+
+MODULE_AUTHOR("http://rt2x00.serialmonkey.com");
+MODULE_VERSION("1.0");
+MODULE_DESCRIPTION("EEPROM 93cx6 chip driver");
+MODULE_LICENSE("GPL");
+
+static inline void eeprom_93cx6_pulse_high(struct eeprom_93cx6 *eeprom)
+{
+ eeprom->reg_data_clock = 1;
+ eeprom->register_write(eeprom);
+
+ /*
+ * Add a short delay for the pulse to work.
+ * According to the specifications the "maximum minimum"
+ * time should be 450ns.
+ */
+ ndelay(450);
+}
+
+static inline void eeprom_93cx6_pulse_low(struct eeprom_93cx6 *eeprom)
+{
+ eeprom->reg_data_clock = 0;
+ eeprom->register_write(eeprom);
+
+ /*
+ * Add a short delay for the pulse to work.
+ * According to the specifications the "maximum minimum"
+ * time should be 450ns.
+ */
+ ndelay(450);
+}
+
+static void eeprom_93cx6_startup(struct eeprom_93cx6 *eeprom)
+{
+ /*
+ * Clear all flags, and enable chip select.
+ */
+ eeprom->register_read(eeprom);
+ eeprom->reg_data_in = 0;
+ eeprom->reg_data_out = 0;
+ eeprom->reg_data_clock = 0;
+ eeprom->reg_chip_select = 1;
+ eeprom->register_write(eeprom);
+
+ /*
+ * kick a pulse.
+ */
+ eeprom_93cx6_pulse_high(eeprom);
+ eeprom_93cx6_pulse_low(eeprom);
+}
+
+static void eeprom_93cx6_cleanup(struct eeprom_93cx6 *eeprom)
+{
+ /*
+ * Clear chip_select and data_in flags.
+ */
+ eeprom->register_read(eeprom);
+ eeprom->reg_data_in = 0;
+ eeprom->reg_chip_select = 0;
+ eeprom->register_write(eeprom);
+
+ /*
+ * kick a pulse.
+ */
+ eeprom_93cx6_pulse_high(eeprom);
+ eeprom_93cx6_pulse_low(eeprom);
+}
+
+static void eeprom_93cx6_write_bits(struct eeprom_93cx6 *eeprom,
+ const u16 data, const u16 count)
+{
+ unsigned int i;
+
+ eeprom->register_read(eeprom);
+
+ /*
+ * Clear data flags.
+ */
+ eeprom->reg_data_in = 0;
+ eeprom->reg_data_out = 0;
+
+ /*
+ * Start writing all bits.
+ */
+ for (i = count; i > 0; i--) {
+ /*
+ * Check if this bit needs to be set.
+ */
+ eeprom->reg_data_in = !!(data & (1 << (i - 1)));
+
+ /*
+ * Write the bit to the eeprom register.
+ */
+ eeprom->register_write(eeprom);
+
+ /*
+ * Kick a pulse.
+ */
+ eeprom_93cx6_pulse_high(eeprom);
+ eeprom_93cx6_pulse_low(eeprom);
+ }
+
+ eeprom->reg_data_in = 0;
+ eeprom->register_write(eeprom);
+}
+
+static void eeprom_93cx6_read_bits(struct eeprom_93cx6 *eeprom,
+ u16 *data, const u16 count)
+{
+ unsigned int i;
+ u16 buf = 0;
+
+ eeprom->register_read(eeprom);
+
+ /*
+ * Clear data flags.
+ */
+ eeprom->reg_data_in = 0;
+ eeprom->reg_data_out = 0;
+
+ /*
+ * Start reading all bits.
+ */
+ for (i = count; i > 0; i--) {
+ eeprom_93cx6_pulse_high(eeprom);
+
+ eeprom->register_read(eeprom);
+
+ /*
+ * Clear data_in flag.
+ */
+ eeprom->reg_data_in = 0;
+
+ /*
+ * Read if the bit has been set.
+ */
+ if (eeprom->reg_data_out)
+ buf |= (1 << (i - 1));
+
+ eeprom_93cx6_pulse_low(eeprom);
+ }
+
+ *data = buf;
+}
+
+/**
+ * eeprom_93cx6_read - Read multiple words from eeprom
+ * @eeprom: Pointer to eeprom structure
+ * @word: Word index from where we should start reading
+ * @data: target pointer where the information will have to be stored
+ *
+ * This function will read the eeprom data as host-endian word
+ * into the given data pointer.
+ */
+void eeprom_93cx6_read(struct eeprom_93cx6 *eeprom, const u8 word,
+ u16 *data)
+{
+ u16 command;
+
+ /*
+ * Initialize the eeprom register
+ */
+ eeprom_93cx6_startup(eeprom);
+
+ /*
+ * Select the read opcode and the word to be read.
+ */
+ command = (PCI_EEPROM_READ_OPCODE << eeprom->width) | word;
+ eeprom_93cx6_write_bits(eeprom, command,
+ PCI_EEPROM_WIDTH_OPCODE + eeprom->width);
+
+ /*
+ * Read the requested 16 bits.
+ */
+ eeprom_93cx6_read_bits(eeprom, data, 16);
+
+ /*
+ * Cleanup eeprom register.
+ */
+ eeprom_93cx6_cleanup(eeprom);
+}
+EXPORT_SYMBOL_GPL(eeprom_93cx6_read);
+
+/**
+ * eeprom_93cx6_multiread - Read multiple words from eeprom
+ * @eeprom: Pointer to eeprom structure
+ * @word: Word index from where we should start reading
+ * @data: target pointer where the information will have to be stored
+ * @words: Number of words that should be read.
+ *
+ * This function will read all requested words from the eeprom,
+ * this is done by calling eeprom_93cx6_read() multiple times.
+ * But with the additional change that while the eeprom_93cx6_read
+ * will return host ordered bytes, this method will return little
+ * endian words.
+ */
+void eeprom_93cx6_multiread(struct eeprom_93cx6 *eeprom, const u8 word,
+ __le16 *data, const u16 words)
+{
+ unsigned int i;
+ u16 tmp;
+
+ for (i = 0; i < words; i++) {
+ tmp = 0;
+ eeprom_93cx6_read(eeprom, word + i, &tmp);
+ data[i] = cpu_to_le16(tmp);
+ }
+}
+EXPORT_SYMBOL_GPL(eeprom_93cx6_multiread);
+
diff --git a/drivers/net/ethernet/atheros/Kconfig b/drivers/net/ethernet/atheros/Kconfig
new file mode 100644
index 0000000..1ed886d
--- /dev/null
+++ b/drivers/net/ethernet/atheros/Kconfig
@@ -0,0 +1,70 @@
+#
+# Atheros device configuration
+#
+
+config NET_VENDOR_ATHEROS
+ bool "Atheros devices"
+ default y
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Atheros devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_ATHEROS
+
+config ATL2
+ tristate "Atheros L2 Fast Ethernet support"
+ depends on PCI
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ This driver supports the Atheros L2 fast ethernet adapter.
+
+ To compile this driver as a module, choose M here. The module
+ will be called atl2.
+
+config ATL1
+ tristate "Atheros/Attansic L1 Gigabit Ethernet support"
+ depends on PCI
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ This driver supports the Atheros/Attansic L1 gigabit ethernet
+ adapter.
+
+ To compile this driver as a module, choose M here. The module
+ will be called atl1.
+
+config ATL1E
+ tristate "Atheros L1E Gigabit Ethernet support (EXPERIMENTAL)"
+ depends on PCI && EXPERIMENTAL
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ This driver supports the Atheros L1E gigabit ethernet adapter.
+
+ To compile this driver as a module, choose M here. The module
+ will be called atl1e.
+
+config ATL1C
+ tristate "Atheros L1C Gigabit Ethernet support (EXPERIMENTAL)"
+ depends on PCI && EXPERIMENTAL
+ select CRC32
+ select NET_CORE
+ select MII
+ ---help---
+ This driver supports the Atheros L1C gigabit ethernet adapter.
+
+ To compile this driver as a module, choose M here. The module
+ will be called atl1c.
+
+endif # NET_VENDOR_ATHEROS
diff --git a/drivers/net/ethernet/atheros/Makefile b/drivers/net/ethernet/atheros/Makefile
new file mode 100644
index 0000000..e7e76fb
--- /dev/null
+++ b/drivers/net/ethernet/atheros/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the Atheros network device drivers.
+#
+
+obj-$(CONFIG_ATL1) += atlx/
+obj-$(CONFIG_ATL2) += atlx/
+obj-$(CONFIG_ATL1E) += atl1e/
+obj-$(CONFIG_ATL1C) += atl1c/
diff --git a/drivers/net/ethernet/atheros/atl1c/Makefile b/drivers/net/ethernet/atheros/atl1c/Makefile
new file mode 100644
index 0000000..c37d966
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atl1c/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_ATL1C) += atl1c.o
+atl1c-objs := atl1c_main.o atl1c_hw.o atl1c_ethtool.o
diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c.h b/drivers/net/ethernet/atheros/atl1c/atl1c.h
new file mode 100644
index 0000000..ca70e16
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c.h
@@ -0,0 +1,636 @@
+/*
+ * Copyright(c) 2008 - 2009 Atheros Corporation. All rights reserved.
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ */
+
+#ifndef _ATL1C_H_
+#define _ATL1C_H_
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/udp.h>
+#include <linux/mii.h>
+#include <linux/io.h>
+#include <linux/vmalloc.h>
+#include <linux/pagemap.h>
+#include <linux/tcp.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+#include <linux/workqueue.h>
+#include <net/checksum.h>
+#include <net/ip6_checksum.h>
+
+#include "atl1c_hw.h"
+
+/* Wake Up Filter Control */
+#define AT_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */
+#define AT_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */
+#define AT_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */
+#define AT_WUFC_MC 0x00000008 /* Multicast Wakeup Enable */
+#define AT_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */
+
+#define AT_VLAN_TO_TAG(_vlan, _tag) \
+ _tag = ((((_vlan) >> 8) & 0xFF) |\
+ (((_vlan) & 0xFF) << 8))
+
+#define AT_TAG_TO_VLAN(_tag, _vlan) \
+ _vlan = ((((_tag) >> 8) & 0xFF) |\
+ (((_tag) & 0xFF) << 8))
+
+#define SPEED_0 0xffff
+#define HALF_DUPLEX 1
+#define FULL_DUPLEX 2
+
+#define AT_RX_BUF_SIZE (ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN)
+#define MAX_JUMBO_FRAME_SIZE (6*1024)
+#define MAX_TSO_FRAME_SIZE (7*1024)
+#define MAX_TX_OFFLOAD_THRESH (9*1024)
+
+#define AT_MAX_RECEIVE_QUEUE 4
+#define AT_DEF_RECEIVE_QUEUE 1
+#define AT_MAX_TRANSMIT_QUEUE 2
+
+#define AT_DMA_HI_ADDR_MASK 0xffffffff00000000ULL
+#define AT_DMA_LO_ADDR_MASK 0x00000000ffffffffULL
+
+#define AT_TX_WATCHDOG (5 * HZ)
+#define AT_MAX_INT_WORK 5
+#define AT_TWSI_EEPROM_TIMEOUT 100
+#define AT_HW_MAX_IDLE_DELAY 10
+#define AT_SUSPEND_LINK_TIMEOUT 100
+
+#define AT_ASPM_L0S_TIMER 6
+#define AT_ASPM_L1_TIMER 12
+#define AT_LCKDET_TIMER 12
+
+#define ATL1C_PCIE_L0S_L1_DISABLE 0x01
+#define ATL1C_PCIE_PHY_RESET 0x02
+
+#define ATL1C_ASPM_L0s_ENABLE 0x0001
+#define ATL1C_ASPM_L1_ENABLE 0x0002
+
+#define AT_REGS_LEN (75 * sizeof(u32))
+#define AT_EEPROM_LEN 512
+
+#define ATL1C_GET_DESC(R, i, type) (&(((type *)((R)->desc))[i]))
+#define ATL1C_RFD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_rx_free_desc)
+#define ATL1C_TPD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_tpd_desc)
+#define ATL1C_RRD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_recv_ret_status)
+
+/* tpd word 1 bit 0:7 General Checksum task offload */
+#define TPD_L4HDR_OFFSET_MASK 0x00FF
+#define TPD_L4HDR_OFFSET_SHIFT 0
+
+/* tpd word 1 bit 0:7 Large Send task offload (IPv4/IPV6) */
+#define TPD_TCPHDR_OFFSET_MASK 0x00FF
+#define TPD_TCPHDR_OFFSET_SHIFT 0
+
+/* tpd word 1 bit 0:7 Custom Checksum task offload */
+#define TPD_PLOADOFFSET_MASK 0x00FF
+#define TPD_PLOADOFFSET_SHIFT 0
+
+/* tpd word 1 bit 8:17 */
+#define TPD_CCSUM_EN_MASK 0x0001
+#define TPD_CCSUM_EN_SHIFT 8
+#define TPD_IP_CSUM_MASK 0x0001
+#define TPD_IP_CSUM_SHIFT 9
+#define TPD_TCP_CSUM_MASK 0x0001
+#define TPD_TCP_CSUM_SHIFT 10
+#define TPD_UDP_CSUM_MASK 0x0001
+#define TPD_UDP_CSUM_SHIFT 11
+#define TPD_LSO_EN_MASK 0x0001 /* TCP Large Send Offload */
+#define TPD_LSO_EN_SHIFT 12
+#define TPD_LSO_VER_MASK 0x0001
+#define TPD_LSO_VER_SHIFT 13 /* 0 : ipv4; 1 : ipv4/ipv6 */
+#define TPD_CON_VTAG_MASK 0x0001
+#define TPD_CON_VTAG_SHIFT 14
+#define TPD_INS_VTAG_MASK 0x0001
+#define TPD_INS_VTAG_SHIFT 15
+#define TPD_IPV4_PACKET_MASK 0x0001 /* valid when LSO VER is 1 */
+#define TPD_IPV4_PACKET_SHIFT 16
+#define TPD_ETH_TYPE_MASK 0x0001
+#define TPD_ETH_TYPE_SHIFT 17 /* 0 : 802.3 frame; 1 : Ethernet */
+
+/* tpd word 18:25 Custom Checksum task offload */
+#define TPD_CCSUM_OFFSET_MASK 0x00FF
+#define TPD_CCSUM_OFFSET_SHIFT 18
+#define TPD_CCSUM_EPAD_MASK 0x0001
+#define TPD_CCSUM_EPAD_SHIFT 30
+
+/* tpd word 18:30 Large Send task offload (IPv4/IPV6) */
+#define TPD_MSS_MASK 0x1FFF
+#define TPD_MSS_SHIFT 18
+
+#define TPD_EOP_MASK 0x0001
+#define TPD_EOP_SHIFT 31
+
+struct atl1c_tpd_desc {
+ __le16 buffer_len; /* include 4-byte CRC */
+ __le16 vlan_tag;
+ __le32 word1;
+ __le64 buffer_addr;
+};
+
+struct atl1c_tpd_ext_desc {
+ u32 reservd_0;
+ __le32 word1;
+ __le32 pkt_len;
+ u32 reservd_1;
+};
+/* rrs word 0 bit 0:31 */
+#define RRS_RX_CSUM_MASK 0xFFFF
+#define RRS_RX_CSUM_SHIFT 0
+#define RRS_RX_RFD_CNT_MASK 0x000F
+#define RRS_RX_RFD_CNT_SHIFT 16
+#define RRS_RX_RFD_INDEX_MASK 0x0FFF
+#define RRS_RX_RFD_INDEX_SHIFT 20
+
+/* rrs flag bit 0:16 */
+#define RRS_HEAD_LEN_MASK 0x00FF
+#define RRS_HEAD_LEN_SHIFT 0
+#define RRS_HDS_TYPE_MASK 0x0003
+#define RRS_HDS_TYPE_SHIFT 8
+#define RRS_CPU_NUM_MASK 0x0003
+#define RRS_CPU_NUM_SHIFT 10
+#define RRS_HASH_FLG_MASK 0x000F
+#define RRS_HASH_FLG_SHIFT 12
+
+#define RRS_HDS_TYPE_HEAD 1
+#define RRS_HDS_TYPE_DATA 2
+
+#define RRS_IS_NO_HDS_TYPE(flag) \
+ ((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == 0)
+
+#define RRS_IS_HDS_HEAD(flag) \
+ ((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == \
+ RRS_HDS_TYPE_HEAD)
+
+#define RRS_IS_HDS_DATA(flag) \
+ ((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == \
+ RRS_HDS_TYPE_DATA)
+
+/* rrs word 3 bit 0:31 */
+#define RRS_PKT_SIZE_MASK 0x3FFF
+#define RRS_PKT_SIZE_SHIFT 0
+#define RRS_ERR_L4_CSUM_MASK 0x0001
+#define RRS_ERR_L4_CSUM_SHIFT 14
+#define RRS_ERR_IP_CSUM_MASK 0x0001
+#define RRS_ERR_IP_CSUM_SHIFT 15
+#define RRS_VLAN_INS_MASK 0x0001
+#define RRS_VLAN_INS_SHIFT 16
+#define RRS_PROT_ID_MASK 0x0007
+#define RRS_PROT_ID_SHIFT 17
+#define RRS_RX_ERR_SUM_MASK 0x0001
+#define RRS_RX_ERR_SUM_SHIFT 20
+#define RRS_RX_ERR_CRC_MASK 0x0001
+#define RRS_RX_ERR_CRC_SHIFT 21
+#define RRS_RX_ERR_FAE_MASK 0x0001
+#define RRS_RX_ERR_FAE_SHIFT 22
+#define RRS_RX_ERR_TRUNC_MASK 0x0001
+#define RRS_RX_ERR_TRUNC_SHIFT 23
+#define RRS_RX_ERR_RUNC_MASK 0x0001
+#define RRS_RX_ERR_RUNC_SHIFT 24
+#define RRS_RX_ERR_ICMP_MASK 0x0001
+#define RRS_RX_ERR_ICMP_SHIFT 25
+#define RRS_PACKET_BCAST_MASK 0x0001
+#define RRS_PACKET_BCAST_SHIFT 26
+#define RRS_PACKET_MCAST_MASK 0x0001
+#define RRS_PACKET_MCAST_SHIFT 27
+#define RRS_PACKET_TYPE_MASK 0x0001
+#define RRS_PACKET_TYPE_SHIFT 28
+#define RRS_FIFO_FULL_MASK 0x0001
+#define RRS_FIFO_FULL_SHIFT 29
+#define RRS_802_3_LEN_ERR_MASK 0x0001
+#define RRS_802_3_LEN_ERR_SHIFT 30
+#define RRS_RXD_UPDATED_MASK 0x0001
+#define RRS_RXD_UPDATED_SHIFT 31
+
+#define RRS_ERR_L4_CSUM 0x00004000
+#define RRS_ERR_IP_CSUM 0x00008000
+#define RRS_VLAN_INS 0x00010000
+#define RRS_RX_ERR_SUM 0x00100000
+#define RRS_RX_ERR_CRC 0x00200000
+#define RRS_802_3_LEN_ERR 0x40000000
+#define RRS_RXD_UPDATED 0x80000000
+
+#define RRS_PACKET_TYPE_802_3 1
+#define RRS_PACKET_TYPE_ETH 0
+#define RRS_PACKET_IS_ETH(word) \
+ ((((word) >> RRS_PACKET_TYPE_SHIFT) & RRS_PACKET_TYPE_MASK) == \
+ RRS_PACKET_TYPE_ETH)
+#define RRS_RXD_IS_VALID(word) \
+ ((((word) >> RRS_RXD_UPDATED_SHIFT) & RRS_RXD_UPDATED_MASK) == 1)
+
+#define RRS_PACKET_PROT_IS_IPV4_ONLY(word) \
+ ((((word) >> RRS_PROT_ID_SHIFT) & RRS_PROT_ID_MASK) == 1)
+#define RRS_PACKET_PROT_IS_IPV6_ONLY(word) \
+ ((((word) >> RRS_PROT_ID_SHIFT) & RRS_PROT_ID_MASK) == 6)
+
+struct atl1c_recv_ret_status {
+ __le32 word0;
+ __le32 rss_hash;
+ __le16 vlan_tag;
+ __le16 flag;
+ __le32 word3;
+};
+
+/* RFD descriptor */
+struct atl1c_rx_free_desc {
+ __le64 buffer_addr;
+};
+
+/* DMA Order Settings */
+enum atl1c_dma_order {
+ atl1c_dma_ord_in = 1,
+ atl1c_dma_ord_enh = 2,
+ atl1c_dma_ord_out = 4
+};
+
+enum atl1c_dma_rcb {
+ atl1c_rcb_64 = 0,
+ atl1c_rcb_128 = 1
+};
+
+enum atl1c_mac_speed {
+ atl1c_mac_speed_0 = 0,
+ atl1c_mac_speed_10_100 = 1,
+ atl1c_mac_speed_1000 = 2
+};
+
+enum atl1c_dma_req_block {
+ atl1c_dma_req_128 = 0,
+ atl1c_dma_req_256 = 1,
+ atl1c_dma_req_512 = 2,
+ atl1c_dma_req_1024 = 3,
+ atl1c_dma_req_2048 = 4,
+ atl1c_dma_req_4096 = 5
+};
+
+enum atl1c_rss_mode {
+ atl1c_rss_mode_disable = 0,
+ atl1c_rss_sig_que = 1,
+ atl1c_rss_mul_que_sig_int = 2,
+ atl1c_rss_mul_que_mul_int = 4,
+};
+
+enum atl1c_rss_type {
+ atl1c_rss_disable = 0,
+ atl1c_rss_ipv4 = 1,
+ atl1c_rss_ipv4_tcp = 2,
+ atl1c_rss_ipv6 = 4,
+ atl1c_rss_ipv6_tcp = 8
+};
+
+enum atl1c_nic_type {
+ athr_l1c = 0,
+ athr_l2c = 1,
+ athr_l2c_b,
+ athr_l2c_b2,
+ athr_l1d,
+ athr_l1d_2,
+};
+
+enum atl1c_trans_queue {
+ atl1c_trans_normal = 0,
+ atl1c_trans_high = 1
+};
+
+struct atl1c_hw_stats {
+ /* rx */
+ unsigned long rx_ok; /* The number of good packet received. */
+ unsigned long rx_bcast; /* The number of good broadcast packet received. */
+ unsigned long rx_mcast; /* The number of good multicast packet received. */
+ unsigned long rx_pause; /* The number of Pause packet received. */
+ unsigned long rx_ctrl; /* The number of Control packet received other than Pause frame. */
+ unsigned long rx_fcs_err; /* The number of packets with bad FCS. */
+ unsigned long rx_len_err; /* The number of packets with mismatch of length field and actual size. */
+ unsigned long rx_byte_cnt; /* The number of bytes of good packet received. FCS is NOT included. */
+ unsigned long rx_runt; /* The number of packets received that are less than 64 byte long and with good FCS. */
+ unsigned long rx_frag; /* The number of packets received that are less than 64 byte long and with bad FCS. */
+ unsigned long rx_sz_64; /* The number of good and bad packets received that are 64 byte long. */
+ unsigned long rx_sz_65_127; /* The number of good and bad packets received that are between 65 and 127-byte long. */
+ unsigned long rx_sz_128_255; /* The number of good and bad packets received that are between 128 and 255-byte long. */
+ unsigned long rx_sz_256_511; /* The number of good and bad packets received that are between 256 and 511-byte long. */
+ unsigned long rx_sz_512_1023; /* The number of good and bad packets received that are between 512 and 1023-byte long. */
+ unsigned long rx_sz_1024_1518; /* The number of good and bad packets received that are between 1024 and 1518-byte long. */
+ unsigned long rx_sz_1519_max; /* The number of good and bad packets received that are between 1519-byte and MTU. */
+ unsigned long rx_sz_ov; /* The number of good and bad packets received that are more than MTU size truncated by Selene. */
+ unsigned long rx_rxf_ov; /* The number of frame dropped due to occurrence of RX FIFO overflow. */
+ unsigned long rx_rrd_ov; /* The number of frame dropped due to occurrence of RRD overflow. */
+ unsigned long rx_align_err; /* Alignment Error */
+ unsigned long rx_bcast_byte_cnt; /* The byte count of broadcast packet received, excluding FCS. */
+ unsigned long rx_mcast_byte_cnt; /* The byte count of multicast packet received, excluding FCS. */
+ unsigned long rx_err_addr; /* The number of packets dropped due to address filtering. */
+
+ /* tx */
+ unsigned long tx_ok; /* The number of good packet transmitted. */
+ unsigned long tx_bcast; /* The number of good broadcast packet transmitted. */
+ unsigned long tx_mcast; /* The number of good multicast packet transmitted. */
+ unsigned long tx_pause; /* The number of Pause packet transmitted. */
+ unsigned long tx_exc_defer; /* The number of packets transmitted with excessive deferral. */
+ unsigned long tx_ctrl; /* The number of packets transmitted is a control frame, excluding Pause frame. */
+ unsigned long tx_defer; /* The number of packets transmitted that is deferred. */
+ unsigned long tx_byte_cnt; /* The number of bytes of data transmitted. FCS is NOT included. */
+ unsigned long tx_sz_64; /* The number of good and bad packets transmitted that are 64 byte long. */
+ unsigned long tx_sz_65_127; /* The number of good and bad packets transmitted that are between 65 and 127-byte long. */
+ unsigned long tx_sz_128_255; /* The number of good and bad packets transmitted that are between 128 and 255-byte long. */
+ unsigned long tx_sz_256_511; /* The number of good and bad packets transmitted that are between 256 and 511-byte long. */
+ unsigned long tx_sz_512_1023; /* The number of good and bad packets transmitted that are between 512 and 1023-byte long. */
+ unsigned long tx_sz_1024_1518; /* The number of good and bad packets transmitted that are between 1024 and 1518-byte long. */
+ unsigned long tx_sz_1519_max; /* The number of good and bad packets transmitted that are between 1519-byte and MTU. */
+ unsigned long tx_1_col; /* The number of packets subsequently transmitted successfully with a single prior collision. */
+ unsigned long tx_2_col; /* The number of packets subsequently transmitted successfully with multiple prior collisions. */
+ unsigned long tx_late_col; /* The number of packets transmitted with late collisions. */
+ unsigned long tx_abort_col; /* The number of transmit packets aborted due to excessive collisions. */
+ unsigned long tx_underrun; /* The number of transmit packets aborted due to transmit FIFO underrun, or TRD FIFO underrun */
+ unsigned long tx_rd_eop; /* The number of times that read beyond the EOP into the next frame area when TRD was not written timely */
+ unsigned long tx_len_err; /* The number of transmit packets with length field does NOT match the actual frame size. */
+ unsigned long tx_trunc; /* The number of transmit packets truncated due to size exceeding MTU. */
+ unsigned long tx_bcast_byte; /* The byte count of broadcast packet transmitted, excluding FCS. */
+ unsigned long tx_mcast_byte; /* The byte count of multicast packet transmitted, excluding FCS. */
+};
+
+struct atl1c_hw {
+ u8 __iomem *hw_addr; /* inner register address */
+ struct atl1c_adapter *adapter;
+ enum atl1c_nic_type nic_type;
+ enum atl1c_dma_order dma_order;
+ enum atl1c_dma_rcb rcb_value;
+ enum atl1c_dma_req_block dmar_block;
+ enum atl1c_dma_req_block dmaw_block;
+
+ u16 device_id;
+ u16 vendor_id;
+ u16 subsystem_id;
+ u16 subsystem_vendor_id;
+ u8 revision_id;
+ u16 phy_id1;
+ u16 phy_id2;
+
+ u32 intr_mask;
+ u8 dmaw_dly_cnt;
+ u8 dmar_dly_cnt;
+
+ u8 preamble_len;
+ u16 max_frame_size;
+ u16 min_frame_size;
+
+ enum atl1c_mac_speed mac_speed;
+ bool mac_duplex;
+ bool hibernate;
+ u16 media_type;
+#define MEDIA_TYPE_AUTO_SENSOR 0
+#define MEDIA_TYPE_100M_FULL 1
+#define MEDIA_TYPE_100M_HALF 2
+#define MEDIA_TYPE_10M_FULL 3
+#define MEDIA_TYPE_10M_HALF 4
+
+ u16 autoneg_advertised;
+ u16 mii_autoneg_adv_reg;
+ u16 mii_1000t_ctrl_reg;
+
+ u16 tx_imt; /* TX Interrupt Moderator timer ( 2us resolution) */
+ u16 rx_imt; /* RX Interrupt Moderator timer ( 2us resolution) */
+ u16 ict; /* Interrupt Clear timer (2us resolution) */
+ u16 ctrl_flags;
+#define ATL1C_INTR_CLEAR_ON_READ 0x0001
+#define ATL1C_INTR_MODRT_ENABLE 0x0002
+#define ATL1C_CMB_ENABLE 0x0004
+#define ATL1C_SMB_ENABLE 0x0010
+#define ATL1C_TXQ_MODE_ENHANCE 0x0020
+#define ATL1C_RX_IPV6_CHKSUM 0x0040
+#define ATL1C_ASPM_L0S_SUPPORT 0x0080
+#define ATL1C_ASPM_L1_SUPPORT 0x0100
+#define ATL1C_ASPM_CTRL_MON 0x0200
+#define ATL1C_HIB_DISABLE 0x0400
+#define ATL1C_APS_MODE_ENABLE 0x0800
+#define ATL1C_LINK_EXT_SYNC 0x1000
+#define ATL1C_CLK_GATING_EN 0x2000
+#define ATL1C_FPGA_VERSION 0x8000
+ u16 link_cap_flags;
+#define ATL1C_LINK_CAP_1000M 0x0001
+ u16 cmb_tpd;
+ u16 cmb_rrd;
+ u16 cmb_rx_timer; /* 2us resolution */
+ u16 cmb_tx_timer;
+ u32 smb_timer;
+
+ u16 rrd_thresh; /* Threshold of number of RRD produced to trigger
+ interrupt request */
+ u16 tpd_thresh;
+ u8 tpd_burst; /* Number of TPD to prefetch in cache-aligned burst. */
+ u8 rfd_burst;
+ enum atl1c_rss_type rss_type;
+ enum atl1c_rss_mode rss_mode;
+ u8 rss_hash_bits;
+ u32 base_cpu;
+ u32 indirect_tab;
+ u8 mac_addr[ETH_ALEN];
+ u8 perm_mac_addr[ETH_ALEN];
+
+ bool phy_configured;
+ bool re_autoneg;
+ bool emi_ca;
+};
+
+/*
+ * atl1c_ring_header represents a single, contiguous block of DMA space
+ * mapped for the three descriptor rings (tpd, rfd, rrd) and the two
+ * message blocks (cmb, smb) described below
+ */
+struct atl1c_ring_header {
+ void *desc; /* virtual address */
+ dma_addr_t dma; /* physical address*/
+ unsigned int size; /* length in bytes */
+};
+
+/*
+ * atl1c_buffer is wrapper around a pointer to a socket buffer
+ * so a DMA handle can be stored along with the skb
+ */
+struct atl1c_buffer {
+ struct sk_buff *skb; /* socket buffer */
+ u16 length; /* rx buffer length */
+ u16 flags; /* information of buffer */
+#define ATL1C_BUFFER_FREE 0x0001
+#define ATL1C_BUFFER_BUSY 0x0002
+#define ATL1C_BUFFER_STATE_MASK 0x0003
+
+#define ATL1C_PCIMAP_SINGLE 0x0004
+#define ATL1C_PCIMAP_PAGE 0x0008
+#define ATL1C_PCIMAP_TYPE_MASK 0x000C
+
+#define ATL1C_PCIMAP_TODEVICE 0x0010
+#define ATL1C_PCIMAP_FROMDEVICE 0x0020
+#define ATL1C_PCIMAP_DIRECTION_MASK 0x0030
+ dma_addr_t dma;
+};
+
+#define ATL1C_SET_BUFFER_STATE(buff, state) do { \
+ ((buff)->flags) &= ~ATL1C_BUFFER_STATE_MASK; \
+ ((buff)->flags) |= (state); \
+ } while (0)
+
+#define ATL1C_SET_PCIMAP_TYPE(buff, type, direction) do { \
+ ((buff)->flags) &= ~ATL1C_PCIMAP_TYPE_MASK; \
+ ((buff)->flags) |= (type); \
+ ((buff)->flags) &= ~ATL1C_PCIMAP_DIRECTION_MASK; \
+ ((buff)->flags) |= (direction); \
+ } while (0)
+
+/* transimit packet descriptor (tpd) ring */
+struct atl1c_tpd_ring {
+ void *desc; /* descriptor ring virtual address */
+ dma_addr_t dma; /* descriptor ring physical address */
+ u16 size; /* descriptor ring length in bytes */
+ u16 count; /* number of descriptors in the ring */
+ u16 next_to_use; /* this is protectd by adapter->tx_lock */
+ atomic_t next_to_clean;
+ struct atl1c_buffer *buffer_info;
+};
+
+/* receive free descriptor (rfd) ring */
+struct atl1c_rfd_ring {
+ void *desc; /* descriptor ring virtual address */
+ dma_addr_t dma; /* descriptor ring physical address */
+ u16 size; /* descriptor ring length in bytes */
+ u16 count; /* number of descriptors in the ring */
+ u16 next_to_use;
+ u16 next_to_clean;
+ struct atl1c_buffer *buffer_info;
+};
+
+/* receive return descriptor (rrd) ring */
+struct atl1c_rrd_ring {
+ void *desc; /* descriptor ring virtual address */
+ dma_addr_t dma; /* descriptor ring physical address */
+ u16 size; /* descriptor ring length in bytes */
+ u16 count; /* number of descriptors in the ring */
+ u16 next_to_use;
+ u16 next_to_clean;
+};
+
+struct atl1c_cmb {
+ void *cmb;
+ dma_addr_t dma;
+};
+
+struct atl1c_smb {
+ void *smb;
+ dma_addr_t dma;
+};
+
+/* board specific private data structure */
+struct atl1c_adapter {
+ struct net_device *netdev;
+ struct pci_dev *pdev;
+ struct napi_struct napi;
+ struct atl1c_hw hw;
+ struct atl1c_hw_stats hw_stats;
+ struct mii_if_info mii; /* MII interface info */
+ u16 rx_buffer_len;
+
+ unsigned long flags;
+#define __AT_TESTING 0x0001
+#define __AT_RESETTING 0x0002
+#define __AT_DOWN 0x0003
+ unsigned long work_event;
+#define ATL1C_WORK_EVENT_RESET 0
+#define ATL1C_WORK_EVENT_LINK_CHANGE 1
+ u32 msg_enable;
+
+ bool have_msi;
+ u32 wol;
+ u16 link_speed;
+ u16 link_duplex;
+
+ spinlock_t mdio_lock;
+ spinlock_t tx_lock;
+ atomic_t irq_sem;
+
+ struct work_struct common_task;
+ struct timer_list watchdog_timer;
+ struct timer_list phy_config_timer;
+
+ /* All Descriptor memory */
+ struct atl1c_ring_header ring_header;
+ struct atl1c_tpd_ring tpd_ring[AT_MAX_TRANSMIT_QUEUE];
+ struct atl1c_rfd_ring rfd_ring[AT_MAX_RECEIVE_QUEUE];
+ struct atl1c_rrd_ring rrd_ring[AT_MAX_RECEIVE_QUEUE];
+ struct atl1c_cmb cmb;
+ struct atl1c_smb smb;
+ int num_rx_queues;
+ u32 bd_number; /* board number;*/
+};
+
+#define AT_WRITE_REG(a, reg, value) ( \
+ writel((value), ((a)->hw_addr + reg)))
+
+#define AT_WRITE_FLUSH(a) (\
+ readl((a)->hw_addr))
+
+#define AT_READ_REG(a, reg, pdata) do { \
+ if (unlikely((a)->hibernate)) { \
+ readl((a)->hw_addr + reg); \
+ *(u32 *)pdata = readl((a)->hw_addr + reg); \
+ } else { \
+ *(u32 *)pdata = readl((a)->hw_addr + reg); \
+ } \
+ } while (0)
+
+#define AT_WRITE_REGB(a, reg, value) (\
+ writeb((value), ((a)->hw_addr + reg)))
+
+#define AT_READ_REGB(a, reg) (\
+ readb((a)->hw_addr + reg))
+
+#define AT_WRITE_REGW(a, reg, value) (\
+ writew((value), ((a)->hw_addr + reg)))
+
+#define AT_READ_REGW(a, reg) (\
+ readw((a)->hw_addr + reg))
+
+#define AT_WRITE_REG_ARRAY(a, reg, offset, value) ( \
+ writel((value), (((a)->hw_addr + reg) + ((offset) << 2))))
+
+#define AT_READ_REG_ARRAY(a, reg, offset) ( \
+ readl(((a)->hw_addr + reg) + ((offset) << 2)))
+
+extern char atl1c_driver_name[];
+extern char atl1c_driver_version[];
+
+extern void atl1c_reinit_locked(struct atl1c_adapter *adapter);
+extern s32 atl1c_reset_hw(struct atl1c_hw *hw);
+extern void atl1c_set_ethtool_ops(struct net_device *netdev);
+#endif /* _ATL1C_H_ */
diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c
new file mode 100644
index 0000000..54313c9
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright(c) 2009 - 2009 Atheros Corporation. All rights reserved.
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/slab.h>
+
+#include "atl1c.h"
+
+static int atl1c_get_settings(struct net_device *netdev,
+ struct ethtool_cmd *ecmd)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ struct atl1c_hw *hw = &adapter->hw;
+
+ ecmd->supported = (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_Autoneg |
+ SUPPORTED_TP);
+ if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M)
+ ecmd->supported |= SUPPORTED_1000baseT_Full;
+
+ ecmd->advertising = ADVERTISED_TP;
+
+ ecmd->advertising |= hw->autoneg_advertised;
+
+ ecmd->port = PORT_TP;
+ ecmd->phy_address = 0;
+ ecmd->transceiver = XCVR_INTERNAL;
+
+ if (adapter->link_speed != SPEED_0) {
+ ethtool_cmd_speed_set(ecmd, adapter->link_speed);
+ if (adapter->link_duplex == FULL_DUPLEX)
+ ecmd->duplex = DUPLEX_FULL;
+ else
+ ecmd->duplex = DUPLEX_HALF;
+ } else {
+ ethtool_cmd_speed_set(ecmd, -1);
+ ecmd->duplex = -1;
+ }
+
+ ecmd->autoneg = AUTONEG_ENABLE;
+ return 0;
+}
+
+static int atl1c_set_settings(struct net_device *netdev,
+ struct ethtool_cmd *ecmd)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ struct atl1c_hw *hw = &adapter->hw;
+ u16 autoneg_advertised;
+
+ while (test_and_set_bit(__AT_RESETTING, &adapter->flags))
+ msleep(1);
+
+ if (ecmd->autoneg == AUTONEG_ENABLE) {
+ autoneg_advertised = ADVERTISED_Autoneg;
+ } else {
+ u32 speed = ethtool_cmd_speed(ecmd);
+ if (speed == SPEED_1000) {
+ if (ecmd->duplex != DUPLEX_FULL) {
+ if (netif_msg_link(adapter))
+ dev_warn(&adapter->pdev->dev,
+ "1000M half is invalid\n");
+ clear_bit(__AT_RESETTING, &adapter->flags);
+ return -EINVAL;
+ }
+ autoneg_advertised = ADVERTISED_1000baseT_Full;
+ } else if (speed == SPEED_100) {
+ if (ecmd->duplex == DUPLEX_FULL)
+ autoneg_advertised = ADVERTISED_100baseT_Full;
+ else
+ autoneg_advertised = ADVERTISED_100baseT_Half;
+ } else {
+ if (ecmd->duplex == DUPLEX_FULL)
+ autoneg_advertised = ADVERTISED_10baseT_Full;
+ else
+ autoneg_advertised = ADVERTISED_10baseT_Half;
+ }
+ }
+
+ if (hw->autoneg_advertised != autoneg_advertised) {
+ hw->autoneg_advertised = autoneg_advertised;
+ if (atl1c_restart_autoneg(hw) != 0) {
+ if (netif_msg_link(adapter))
+ dev_warn(&adapter->pdev->dev,
+ "ethtool speed/duplex setting failed\n");
+ clear_bit(__AT_RESETTING, &adapter->flags);
+ return -EINVAL;
+ }
+ }
+ clear_bit(__AT_RESETTING, &adapter->flags);
+ return 0;
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39))
+static u32 atl1c_get_tx_csum(struct net_device *netdev)
+{
+ return (netdev->features & NETIF_F_HW_CSUM) != 0;
+}
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */
+
+static u32 atl1c_get_msglevel(struct net_device *netdev)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ return adapter->msg_enable;
+}
+
+static void atl1c_set_msglevel(struct net_device *netdev, u32 data)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ adapter->msg_enable = data;
+}
+
+static int atl1c_get_regs_len(struct net_device *netdev)
+{
+ return AT_REGS_LEN;
+}
+
+static void atl1c_get_regs(struct net_device *netdev,
+ struct ethtool_regs *regs, void *p)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ struct atl1c_hw *hw = &adapter->hw;
+ u32 *regs_buff = p;
+ u16 phy_data;
+
+ memset(p, 0, AT_REGS_LEN);
+
+ regs->version = 0;
+ AT_READ_REG(hw, REG_VPD_CAP, p++);
+ AT_READ_REG(hw, REG_PM_CTRL, p++);
+ AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL, p++);
+ AT_READ_REG(hw, REG_TWSI_CTRL, p++);
+ AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL, p++);
+ AT_READ_REG(hw, REG_MASTER_CTRL, p++);
+ AT_READ_REG(hw, REG_MANUAL_TIMER_INIT, p++);
+ AT_READ_REG(hw, REG_IRQ_MODRT_TIMER_INIT, p++);
+ AT_READ_REG(hw, REG_GPHY_CTRL, p++);
+ AT_READ_REG(hw, REG_LINK_CTRL, p++);
+ AT_READ_REG(hw, REG_IDLE_STATUS, p++);
+ AT_READ_REG(hw, REG_MDIO_CTRL, p++);
+ AT_READ_REG(hw, REG_SERDES_LOCK, p++);
+ AT_READ_REG(hw, REG_MAC_CTRL, p++);
+ AT_READ_REG(hw, REG_MAC_IPG_IFG, p++);
+ AT_READ_REG(hw, REG_MAC_STA_ADDR, p++);
+ AT_READ_REG(hw, REG_MAC_STA_ADDR+4, p++);
+ AT_READ_REG(hw, REG_RX_HASH_TABLE, p++);
+ AT_READ_REG(hw, REG_RX_HASH_TABLE+4, p++);
+ AT_READ_REG(hw, REG_RXQ_CTRL, p++);
+ AT_READ_REG(hw, REG_TXQ_CTRL, p++);
+ AT_READ_REG(hw, REG_MTU, p++);
+ AT_READ_REG(hw, REG_WOL_CTRL, p++);
+
+ atl1c_read_phy_reg(hw, MII_BMCR, &phy_data);
+ regs_buff[73] = (u32) phy_data;
+ atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
+ regs_buff[74] = (u32) phy_data;
+}
+
+static int atl1c_get_eeprom_len(struct net_device *netdev)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+
+ if (atl1c_check_eeprom_exist(&adapter->hw))
+ return AT_EEPROM_LEN;
+ else
+ return 0;
+}
+
+static int atl1c_get_eeprom(struct net_device *netdev,
+ struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ struct atl1c_hw *hw = &adapter->hw;
+ u32 *eeprom_buff;
+ int first_dword, last_dword;
+ int ret_val = 0;
+ int i;
+
+ if (eeprom->len == 0)
+ return -EINVAL;
+
+ if (!atl1c_check_eeprom_exist(hw)) /* not exist */
+ return -EINVAL;
+
+ eeprom->magic = adapter->pdev->vendor |
+ (adapter->pdev->device << 16);
+
+ first_dword = eeprom->offset >> 2;
+ last_dword = (eeprom->offset + eeprom->len - 1) >> 2;
+
+ eeprom_buff = kmalloc(sizeof(u32) *
+ (last_dword - first_dword + 1), GFP_KERNEL);
+ if (eeprom_buff == NULL)
+ return -ENOMEM;
+
+ for (i = first_dword; i < last_dword; i++) {
+ if (!atl1c_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) {
+ kfree(eeprom_buff);
+ return -EIO;
+ }
+ }
+
+ memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3),
+ eeprom->len);
+ kfree(eeprom_buff);
+
+ return ret_val;
+ return 0;
+}
+
+static void atl1c_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+
+ strlcpy(drvinfo->driver, atl1c_driver_name, sizeof(drvinfo->driver));
+ strlcpy(drvinfo->version, atl1c_driver_version,
+ sizeof(drvinfo->version));
+ strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
+ strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
+ sizeof(drvinfo->bus_info));
+ drvinfo->n_stats = 0;
+ drvinfo->testinfo_len = 0;
+ drvinfo->regdump_len = atl1c_get_regs_len(netdev);
+ drvinfo->eedump_len = atl1c_get_eeprom_len(netdev);
+}
+
+static void atl1c_get_wol(struct net_device *netdev,
+ struct ethtool_wolinfo *wol)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+
+ wol->supported = WAKE_MAGIC | WAKE_PHY;
+ wol->wolopts = 0;
+
+ if (adapter->wol & AT_WUFC_EX)
+ wol->wolopts |= WAKE_UCAST;
+ if (adapter->wol & AT_WUFC_MC)
+ wol->wolopts |= WAKE_MCAST;
+ if (adapter->wol & AT_WUFC_BC)
+ wol->wolopts |= WAKE_BCAST;
+ if (adapter->wol & AT_WUFC_MAG)
+ wol->wolopts |= WAKE_MAGIC;
+ if (adapter->wol & AT_WUFC_LNKC)
+ wol->wolopts |= WAKE_PHY;
+}
+
+static int atl1c_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+
+ if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE |
+ WAKE_UCAST | WAKE_BCAST | WAKE_MCAST))
+ return -EOPNOTSUPP;
+ /* these settings will always override what we currently have */
+ adapter->wol = 0;
+
+ if (wol->wolopts & WAKE_MAGIC)
+ adapter->wol |= AT_WUFC_MAG;
+ if (wol->wolopts & WAKE_PHY)
+ adapter->wol |= AT_WUFC_LNKC;
+
+ device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
+
+ return 0;
+}
+
+static int atl1c_nway_reset(struct net_device *netdev)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ if (netif_running(netdev))
+ atl1c_reinit_locked(adapter);
+ return 0;
+}
+
+static const struct ethtool_ops atl1c_ethtool_ops = {
+ .get_settings = atl1c_get_settings,
+ .set_settings = atl1c_set_settings,
+ .get_drvinfo = atl1c_get_drvinfo,
+ .get_regs_len = atl1c_get_regs_len,
+ .get_regs = atl1c_get_regs,
+ .get_wol = atl1c_get_wol,
+ .set_wol = atl1c_set_wol,
+ .get_msglevel = atl1c_get_msglevel,
+ .set_msglevel = atl1c_set_msglevel,
+ .nway_reset = atl1c_nway_reset,
+ .get_link = ethtool_op_get_link,
+ .get_eeprom_len = atl1c_get_eeprom_len,
+ .get_eeprom = atl1c_get_eeprom,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39))
+ .get_tx_csum = atl1c_get_tx_csum,
+ .get_sg = ethtool_op_get_sg,
+ .set_sg = ethtool_op_set_sg,
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */
+};
+
+void atl1c_set_ethtool_ops(struct net_device *netdev)
+{
+ SET_ETHTOOL_OPS(netdev, &atl1c_ethtool_ops);
+}
diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c
new file mode 100644
index 0000000..23f2ab0
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c
@@ -0,0 +1,662 @@
+/*
+ * Copyright(c) 2007 Atheros Corporation. All rights reserved.
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ */
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mii.h>
+#include <linux/crc32.h>
+
+#include "atl1c.h"
+
+/*
+ * check_eeprom_exist
+ * return 1 if eeprom exist
+ */
+int atl1c_check_eeprom_exist(struct atl1c_hw *hw)
+{
+ u32 data;
+
+ AT_READ_REG(hw, REG_TWSI_DEBUG, &data);
+ if (data & TWSI_DEBUG_DEV_EXIST)
+ return 1;
+
+ AT_READ_REG(hw, REG_MASTER_CTRL, &data);
+ if (data & MASTER_CTRL_OTP_SEL)
+ return 1;
+ return 0;
+}
+
+void atl1c_hw_set_mac_addr(struct atl1c_hw *hw)
+{
+ u32 value;
+ /*
+ * 00-0B-6A-F6-00-DC
+ * 0: 6AF600DC 1: 000B
+ * low dword
+ */
+ value = (((u32)hw->mac_addr[2]) << 24) |
+ (((u32)hw->mac_addr[3]) << 16) |
+ (((u32)hw->mac_addr[4]) << 8) |
+ (((u32)hw->mac_addr[5])) ;
+ AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value);
+ /* hight dword */
+ value = (((u32)hw->mac_addr[0]) << 8) |
+ (((u32)hw->mac_addr[1])) ;
+ AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value);
+}
+
+/*
+ * atl1c_get_permanent_address
+ * return 0 if get valid mac address,
+ */
+static int atl1c_get_permanent_address(struct atl1c_hw *hw)
+{
+ u32 addr[2];
+ u32 i;
+ u32 otp_ctrl_data;
+ u32 twsi_ctrl_data;
+ u32 ltssm_ctrl_data;
+ u32 wol_data;
+ u8 eth_addr[ETH_ALEN];
+ u16 phy_data;
+ bool raise_vol = false;
+
+ /* init */
+ addr[0] = addr[1] = 0;
+ AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data);
+ if (atl1c_check_eeprom_exist(hw)) {
+ if (hw->nic_type == athr_l1c || hw->nic_type == athr_l2c) {
+ /* Enable OTP CLK */
+ if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) {
+ otp_ctrl_data |= OTP_CTRL_CLK_EN;
+ AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
+ AT_WRITE_FLUSH(hw);
+ msleep(1);
+ }
+ }
+
+ if (hw->nic_type == athr_l2c_b ||
+ hw->nic_type == athr_l2c_b2 ||
+ hw->nic_type == athr_l1d) {
+ atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x00);
+ if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &phy_data))
+ goto out;
+ phy_data &= 0xFF7F;
+ atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data);
+
+ atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x3B);
+ if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &phy_data))
+ goto out;
+ phy_data |= 0x8;
+ atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data);
+ udelay(20);
+ raise_vol = true;
+ }
+ /* close open bit of ReadOnly*/
+ AT_READ_REG(hw, REG_LTSSM_ID_CTRL, <ssm_ctrl_data);
+ ltssm_ctrl_data &= ~LTSSM_ID_EN_WRO;
+ AT_WRITE_REG(hw, REG_LTSSM_ID_CTRL, ltssm_ctrl_data);
+
+ /* clear any WOL settings */
+ AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
+ AT_READ_REG(hw, REG_WOL_CTRL, &wol_data);
+
+
+ AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data);
+ twsi_ctrl_data |= TWSI_CTRL_SW_LDSTART;
+ AT_WRITE_REG(hw, REG_TWSI_CTRL, twsi_ctrl_data);
+ for (i = 0; i < AT_TWSI_EEPROM_TIMEOUT; i++) {
+ msleep(10);
+ AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data);
+ if ((twsi_ctrl_data & TWSI_CTRL_SW_LDSTART) == 0)
+ break;
+ }
+ if (i >= AT_TWSI_EEPROM_TIMEOUT)
+ return -1;
+ }
+ /* Disable OTP_CLK */
+ if ((hw->nic_type == athr_l1c || hw->nic_type == athr_l2c)) {
+ otp_ctrl_data &= ~OTP_CTRL_CLK_EN;
+ AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
+ msleep(1);
+ }
+ if (raise_vol) {
+ if (hw->nic_type == athr_l2c_b ||
+ hw->nic_type == athr_l2c_b2 ||
+ hw->nic_type == athr_l1d ||
+ hw->nic_type == athr_l1d_2) {
+ atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x00);
+ if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &phy_data))
+ goto out;
+ phy_data |= 0x80;
+ atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data);
+
+ atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x3B);
+ if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &phy_data))
+ goto out;
+ phy_data &= 0xFFF7;
+ atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data);
+ udelay(20);
+ }
+ }
+
+ /* maybe MAC-address is from BIOS */
+ AT_READ_REG(hw, REG_MAC_STA_ADDR, &addr[0]);
+ AT_READ_REG(hw, REG_MAC_STA_ADDR + 4, &addr[1]);
+ *(u32 *) ð_addr[2] = swab32(addr[0]);
+ *(u16 *) ð_addr[0] = swab16(*(u16 *)&addr[1]);
+
+ if (is_valid_ether_addr(eth_addr)) {
+ memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN);
+ return 0;
+ }
+
+out:
+ return -1;
+}
+
+bool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value)
+{
+ int i;
+ int ret = false;
+ u32 otp_ctrl_data;
+ u32 control;
+ u32 data;
+
+ if (offset & 3)
+ return ret; /* address do not align */
+
+ AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data);
+ if (!(otp_ctrl_data & OTP_CTRL_CLK_EN))
+ AT_WRITE_REG(hw, REG_OTP_CTRL,
+ (otp_ctrl_data | OTP_CTRL_CLK_EN));
+
+ AT_WRITE_REG(hw, REG_EEPROM_DATA_LO, 0);
+ control = (offset & EEPROM_CTRL_ADDR_MASK) << EEPROM_CTRL_ADDR_SHIFT;
+ AT_WRITE_REG(hw, REG_EEPROM_CTRL, control);
+
+ for (i = 0; i < 10; i++) {
+ udelay(100);
+ AT_READ_REG(hw, REG_EEPROM_CTRL, &control);
+ if (control & EEPROM_CTRL_RW)
+ break;
+ }
+ if (control & EEPROM_CTRL_RW) {
+ AT_READ_REG(hw, REG_EEPROM_CTRL, &data);
+ AT_READ_REG(hw, REG_EEPROM_DATA_LO, p_value);
+ data = data & 0xFFFF;
+ *p_value = swab32((data << 16) | (*p_value >> 16));
+ ret = true;
+ }
+ if (!(otp_ctrl_data & OTP_CTRL_CLK_EN))
+ AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
+
+ return ret;
+}
+/*
+ * Reads the adapter's MAC address from the EEPROM
+ *
+ * hw - Struct containing variables accessed by shared code
+ */
+int atl1c_read_mac_addr(struct atl1c_hw *hw)
+{
+ int err = 0;
+
+ err = atl1c_get_permanent_address(hw);
+ if (err)
+ random_ether_addr(hw->perm_mac_addr);
+
+ memcpy(hw->mac_addr, hw->perm_mac_addr, sizeof(hw->perm_mac_addr));
+ return 0;
+}
+
+/*
+ * atl1c_hash_mc_addr
+ * purpose
+ * set hash value for a multicast address
+ * hash calcu processing :
+ * 1. calcu 32bit CRC for multicast address
+ * 2. reverse crc with MSB to LSB
+ */
+u32 atl1c_hash_mc_addr(struct atl1c_hw *hw, u8 *mc_addr)
+{
+ u32 crc32;
+ u32 value = 0;
+ int i;
+
+ crc32 = ether_crc_le(6, mc_addr);
+ for (i = 0; i < 32; i++)
+ value |= (((crc32 >> i) & 1) << (31 - i));
+
+ return value;
+}
+
+/*
+ * Sets the bit in the multicast table corresponding to the hash value.
+ * hw - Struct containing variables accessed by shared code
+ * hash_value - Multicast address hash value
+ */
+void atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value)
+{
+ u32 hash_bit, hash_reg;
+ u32 mta;
+
+ /*
+ * The HASH Table is a register array of 2 32-bit registers.
+ * It is treated like an array of 64 bits. We want to set
+ * bit BitArray[hash_value]. So we figure out what register
+ * the bit is in, read it, OR in the new bit, then write
+ * back the new value. The register is determined by the
+ * upper bit of the hash value and the bit within that
+ * register are determined by the lower 5 bits of the value.
+ */
+ hash_reg = (hash_value >> 31) & 0x1;
+ hash_bit = (hash_value >> 26) & 0x1F;
+
+ mta = AT_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg);
+
+ mta |= (1 << hash_bit);
+
+ AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta);
+}
+
+/*
+ * Reads the value from a PHY register
+ * hw - Struct containing variables accessed by shared code
+ * reg_addr - address of the PHY register to read
+ */
+int atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data)
+{
+ u32 val;
+ int i;
+
+ val = ((u32)(reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT |
+ MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW |
+ MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT;
+
+ AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
+
+ for (i = 0; i < MDIO_WAIT_TIMES; i++) {
+ udelay(2);
+ AT_READ_REG(hw, REG_MDIO_CTRL, &val);
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ break;
+ }
+ if (!(val & (MDIO_START | MDIO_BUSY))) {
+ *phy_data = (u16)val;
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * Writes a value to a PHY register
+ * hw - Struct containing variables accessed by shared code
+ * reg_addr - address of the PHY register to write
+ * data - data to write to the PHY
+ */
+int atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data)
+{
+ int i;
+ u32 val;
+
+ val = ((u32)(phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT |
+ (reg_addr & MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT |
+ MDIO_SUP_PREAMBLE | MDIO_START |
+ MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT;
+
+ AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
+
+ for (i = 0; i < MDIO_WAIT_TIMES; i++) {
+ udelay(2);
+ AT_READ_REG(hw, REG_MDIO_CTRL, &val);
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ break;
+ }
+
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ return 0;
+
+ return -1;
+}
+
+/*
+ * Configures PHY autoneg and flow control advertisement settings
+ *
+ * hw - Struct containing variables accessed by shared code
+ */
+static int atl1c_phy_setup_adv(struct atl1c_hw *hw)
+{
+ u16 mii_adv_data = ADVERTISE_DEFAULT_CAP & ~ADVERTISE_ALL;
+ u16 mii_giga_ctrl_data = GIGA_CR_1000T_DEFAULT_CAP &
+ ~GIGA_CR_1000T_SPEED_MASK;
+
+ if (hw->autoneg_advertised & ADVERTISED_10baseT_Half)
+ mii_adv_data |= ADVERTISE_10HALF;
+ if (hw->autoneg_advertised & ADVERTISED_10baseT_Full)
+ mii_adv_data |= ADVERTISE_10FULL;
+ if (hw->autoneg_advertised & ADVERTISED_100baseT_Half)
+ mii_adv_data |= ADVERTISE_100HALF;
+ if (hw->autoneg_advertised & ADVERTISED_100baseT_Full)
+ mii_adv_data |= ADVERTISE_100FULL;
+
+ if (hw->autoneg_advertised & ADVERTISED_Autoneg)
+ mii_adv_data |= ADVERTISE_10HALF | ADVERTISE_10FULL |
+ ADVERTISE_100HALF | ADVERTISE_100FULL;
+
+ if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M) {
+ if (hw->autoneg_advertised & ADVERTISED_1000baseT_Half)
+ mii_giga_ctrl_data |= ADVERTISE_1000HALF;
+ if (hw->autoneg_advertised & ADVERTISED_1000baseT_Full)
+ mii_giga_ctrl_data |= ADVERTISE_1000FULL;
+ if (hw->autoneg_advertised & ADVERTISED_Autoneg)
+ mii_giga_ctrl_data |= ADVERTISE_1000HALF |
+ ADVERTISE_1000FULL;
+ }
+
+ if (atl1c_write_phy_reg(hw, MII_ADVERTISE, mii_adv_data) != 0 ||
+ atl1c_write_phy_reg(hw, MII_CTRL1000, mii_giga_ctrl_data) != 0)
+ return -1;
+ return 0;
+}
+
+void atl1c_phy_disable(struct atl1c_hw *hw)
+{
+ AT_WRITE_REGW(hw, REG_GPHY_CTRL,
+ GPHY_CTRL_PW_WOL_DIS | GPHY_CTRL_EXT_RESET);
+}
+
+static void atl1c_phy_magic_data(struct atl1c_hw *hw)
+{
+ u16 data;
+
+ data = ANA_LOOP_SEL_10BT | ANA_EN_MASK_TB | ANA_EN_10BT_IDLE |
+ ((1 & ANA_INTERVAL_SEL_TIMER_MASK) <<
+ ANA_INTERVAL_SEL_TIMER_SHIFT);
+
+ atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_18);
+ atl1c_write_phy_reg(hw, MII_DBG_DATA, data);
+
+ data = (2 & ANA_SERDES_CDR_BW_MASK) | ANA_MS_PAD_DBG |
+ ANA_SERDES_EN_DEEM | ANA_SERDES_SEL_HSP | ANA_SERDES_EN_PLL |
+ ANA_SERDES_EN_LCKDT;
+
+ atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_5);
+ atl1c_write_phy_reg(hw, MII_DBG_DATA, data);
+
+ data = (44 & ANA_LONG_CABLE_TH_100_MASK) |
+ ((33 & ANA_SHORT_CABLE_TH_100_MASK) <<
+ ANA_SHORT_CABLE_TH_100_SHIFT) | ANA_BP_BAD_LINK_ACCUM |
+ ANA_BP_SMALL_BW;
+
+ atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_54);
+ atl1c_write_phy_reg(hw, MII_DBG_DATA, data);
+
+ data = (11 & ANA_IECHO_ADJ_MASK) | ((11 & ANA_IECHO_ADJ_MASK) <<
+ ANA_IECHO_ADJ_2_SHIFT) | ((8 & ANA_IECHO_ADJ_MASK) <<
+ ANA_IECHO_ADJ_1_SHIFT) | ((8 & ANA_IECHO_ADJ_MASK) <<
+ ANA_IECHO_ADJ_0_SHIFT);
+
+ atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_4);
+ atl1c_write_phy_reg(hw, MII_DBG_DATA, data);
+
+ data = ANA_RESTART_CAL | ((7 & ANA_MANUL_SWICH_ON_MASK) <<
+ ANA_MANUL_SWICH_ON_SHIFT) | ANA_MAN_ENABLE |
+ ANA_SEL_HSP | ANA_EN_HB | ANA_OEN_125M;
+
+ atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_0);
+ atl1c_write_phy_reg(hw, MII_DBG_DATA, data);
+
+ if (hw->ctrl_flags & ATL1C_HIB_DISABLE) {
+ atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_41);
+ if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &data) != 0)
+ return;
+ data &= ~ANA_TOP_PS_EN;
+ atl1c_write_phy_reg(hw, MII_DBG_DATA, data);
+
+ atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_11);
+ if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &data) != 0)
+ return;
+ data &= ~ANA_PS_HIB_EN;
+ atl1c_write_phy_reg(hw, MII_DBG_DATA, data);
+ }
+}
+
+int atl1c_phy_reset(struct atl1c_hw *hw)
+{
+ struct atl1c_adapter *adapter = hw->adapter;
+ struct pci_dev *pdev = adapter->pdev;
+ u16 phy_data;
+ u32 phy_ctrl_data = GPHY_CTRL_DEFAULT;
+ u32 mii_ier_data = IER_LINK_UP | IER_LINK_DOWN;
+ int err;
+
+ if (hw->ctrl_flags & ATL1C_HIB_DISABLE)
+ phy_ctrl_data &= ~GPHY_CTRL_HIB_EN;
+
+ AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data);
+ AT_WRITE_FLUSH(hw);
+ msleep(40);
+ phy_ctrl_data |= GPHY_CTRL_EXT_RESET;
+ AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data);
+ AT_WRITE_FLUSH(hw);
+ msleep(10);
+
+ if (hw->nic_type == athr_l2c_b) {
+ atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x0A);
+ atl1c_read_phy_reg(hw, MII_DBG_DATA, &phy_data);
+ atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data & 0xDFFF);
+ }
+
+ if (hw->nic_type == athr_l2c_b ||
+ hw->nic_type == athr_l2c_b2 ||
+ hw->nic_type == athr_l1d ||
+ hw->nic_type == athr_l1d_2) {
+ atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x3B);
+ atl1c_read_phy_reg(hw, MII_DBG_DATA, &phy_data);
+ atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data & 0xFFF7);
+ msleep(20);
+ }
+ if (hw->nic_type == athr_l1d) {
+ atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x29);
+ atl1c_write_phy_reg(hw, MII_DBG_DATA, 0x929D);
+ }
+ if (hw->nic_type == athr_l1c || hw->nic_type == athr_l2c_b2
+ || hw->nic_type == athr_l2c) {
+ atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x29);
+ atl1c_write_phy_reg(hw, MII_DBG_DATA, 0xB6DD);
+ }
+ err = atl1c_write_phy_reg(hw, MII_IER, mii_ier_data);
+ if (err) {
+ if (netif_msg_hw(adapter))
+ dev_err(&pdev->dev,
+ "Error enable PHY linkChange Interrupt\n");
+ return err;
+ }
+ if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION))
+ atl1c_phy_magic_data(hw);
+ return 0;
+}
+
+int atl1c_phy_init(struct atl1c_hw *hw)
+{
+ struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter;
+ struct pci_dev *pdev = adapter->pdev;
+ int ret_val;
+ u16 mii_bmcr_data = BMCR_RESET;
+
+ if ((atl1c_read_phy_reg(hw, MII_PHYSID1, &hw->phy_id1) != 0) ||
+ (atl1c_read_phy_reg(hw, MII_PHYSID2, &hw->phy_id2) != 0)) {
+ dev_err(&pdev->dev, "Error get phy ID\n");
+ return -1;
+ }
+ switch (hw->media_type) {
+ case MEDIA_TYPE_AUTO_SENSOR:
+ ret_val = atl1c_phy_setup_adv(hw);
+ if (ret_val) {
+ if (netif_msg_link(adapter))
+ dev_err(&pdev->dev,
+ "Error Setting up Auto-Negotiation\n");
+ return ret_val;
+ }
+ mii_bmcr_data |= BMCR_ANENABLE | BMCR_ANRESTART;
+ break;
+ case MEDIA_TYPE_100M_FULL:
+ mii_bmcr_data |= BMCR_SPEED100 | BMCR_FULLDPLX;
+ break;
+ case MEDIA_TYPE_100M_HALF:
+ mii_bmcr_data |= BMCR_SPEED100;
+ break;
+ case MEDIA_TYPE_10M_FULL:
+ mii_bmcr_data |= BMCR_FULLDPLX;
+ break;
+ case MEDIA_TYPE_10M_HALF:
+ break;
+ default:
+ if (netif_msg_link(adapter))
+ dev_err(&pdev->dev, "Wrong Media type %d\n",
+ hw->media_type);
+ return -1;
+ break;
+ }
+
+ ret_val = atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data);
+ if (ret_val)
+ return ret_val;
+ hw->phy_configured = true;
+
+ return 0;
+}
+
+/*
+ * Detects the current speed and duplex settings of the hardware.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * speed - Speed of the connection
+ * duplex - Duplex setting of the connection
+ */
+int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex)
+{
+ int err;
+ u16 phy_data;
+
+ /* Read PHY Specific Status Register (17) */
+ err = atl1c_read_phy_reg(hw, MII_GIGA_PSSR, &phy_data);
+ if (err)
+ return err;
+
+ if (!(phy_data & GIGA_PSSR_SPD_DPLX_RESOLVED))
+ return -1;
+
+ switch (phy_data & GIGA_PSSR_SPEED) {
+ case GIGA_PSSR_1000MBS:
+ *speed = SPEED_1000;
+ break;
+ case GIGA_PSSR_100MBS:
+ *speed = SPEED_100;
+ break;
+ case GIGA_PSSR_10MBS:
+ *speed = SPEED_10;
+ break;
+ default:
+ return -1;
+ break;
+ }
+
+ if (phy_data & GIGA_PSSR_DPLX)
+ *duplex = FULL_DUPLEX;
+ else
+ *duplex = HALF_DUPLEX;
+
+ return 0;
+}
+
+int atl1c_phy_power_saving(struct atl1c_hw *hw)
+{
+ struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter;
+ struct pci_dev *pdev = adapter->pdev;
+ int ret = 0;
+ u16 autoneg_advertised = ADVERTISED_10baseT_Half;
+ u16 save_autoneg_advertised;
+ u16 phy_data;
+ u16 mii_lpa_data;
+ u16 speed = SPEED_0;
+ u16 duplex = FULL_DUPLEX;
+ int i;
+
+ atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
+ atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
+ if (phy_data & BMSR_LSTATUS) {
+ atl1c_read_phy_reg(hw, MII_LPA, &mii_lpa_data);
+ if (mii_lpa_data & LPA_10FULL)
+ autoneg_advertised = ADVERTISED_10baseT_Full;
+ else if (mii_lpa_data & LPA_10HALF)
+ autoneg_advertised = ADVERTISED_10baseT_Half;
+ else if (mii_lpa_data & LPA_100HALF)
+ autoneg_advertised = ADVERTISED_100baseT_Half;
+ else if (mii_lpa_data & LPA_100FULL)
+ autoneg_advertised = ADVERTISED_100baseT_Full;
+
+ save_autoneg_advertised = hw->autoneg_advertised;
+ hw->phy_configured = false;
+ hw->autoneg_advertised = autoneg_advertised;
+ if (atl1c_restart_autoneg(hw) != 0) {
+ dev_dbg(&pdev->dev, "phy autoneg failed\n");
+ ret = -1;
+ }
+ hw->autoneg_advertised = save_autoneg_advertised;
+
+ if (mii_lpa_data) {
+ for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) {
+ mdelay(100);
+ atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
+ atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
+ if (phy_data & BMSR_LSTATUS) {
+ if (atl1c_get_speed_and_duplex(hw, &speed,
+ &duplex) != 0)
+ dev_dbg(&pdev->dev,
+ "get speed and duplex failed\n");
+ break;
+ }
+ }
+ }
+ } else {
+ speed = SPEED_10;
+ duplex = HALF_DUPLEX;
+ }
+ adapter->link_speed = speed;
+ adapter->link_duplex = duplex;
+
+ return ret;
+}
+
+int atl1c_restart_autoneg(struct atl1c_hw *hw)
+{
+ int err = 0;
+ u16 mii_bmcr_data = BMCR_RESET;
+
+ err = atl1c_phy_setup_adv(hw);
+ if (err)
+ return err;
+ mii_bmcr_data |= BMCR_ANENABLE | BMCR_ANRESTART;
+
+ return atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data);
+}
diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h
new file mode 100644
index 0000000..655fc6c
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h
@@ -0,0 +1,868 @@
+/*
+ * Copyright(c) 2008 - 2009 Atheros Corporation. All rights reserved.
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ */
+
+#ifndef _ATL1C_HW_H_
+#define _ATL1C_HW_H_
+
+#include <linux/types.h>
+#include <linux/mii.h>
+
+struct atl1c_adapter;
+struct atl1c_hw;
+
+/* function prototype */
+void atl1c_phy_disable(struct atl1c_hw *hw);
+void atl1c_hw_set_mac_addr(struct atl1c_hw *hw);
+int atl1c_phy_reset(struct atl1c_hw *hw);
+int atl1c_read_mac_addr(struct atl1c_hw *hw);
+int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex);
+u32 atl1c_hash_mc_addr(struct atl1c_hw *hw, u8 *mc_addr);
+void atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value);
+int atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data);
+int atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data);
+bool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value);
+int atl1c_phy_init(struct atl1c_hw *hw);
+int atl1c_check_eeprom_exist(struct atl1c_hw *hw);
+int atl1c_restart_autoneg(struct atl1c_hw *hw);
+int atl1c_phy_power_saving(struct atl1c_hw *hw);
+/* register definition */
+#define REG_DEVICE_CAP 0x5C
+#define DEVICE_CAP_MAX_PAYLOAD_MASK 0x7
+#define DEVICE_CAP_MAX_PAYLOAD_SHIFT 0
+
+#define REG_DEVICE_CTRL 0x60
+#define DEVICE_CTRL_MAX_PAYLOAD_MASK 0x7
+#define DEVICE_CTRL_MAX_PAYLOAD_SHIFT 5
+#define DEVICE_CTRL_MAX_RREQ_SZ_MASK 0x7
+#define DEVICE_CTRL_MAX_RREQ_SZ_SHIFT 12
+
+#define REG_LINK_CTRL 0x68
+#define LINK_CTRL_L0S_EN 0x01
+#define LINK_CTRL_L1_EN 0x02
+#define LINK_CTRL_EXT_SYNC 0x80
+
+#define REG_VPD_CAP 0x6C
+#define VPD_CAP_ID_MASK 0xff
+#define VPD_CAP_ID_SHIFT 0
+#define VPD_CAP_NEXT_PTR_MASK 0xFF
+#define VPD_CAP_NEXT_PTR_SHIFT 8
+#define VPD_CAP_VPD_ADDR_MASK 0x7FFF
+#define VPD_CAP_VPD_ADDR_SHIFT 16
+#define VPD_CAP_VPD_FLAG 0x80000000
+
+#define REG_VPD_DATA 0x70
+
+#define REG_PCIE_UC_SEVERITY 0x10C
+#define PCIE_UC_SERVRITY_TRN 0x00000001
+#define PCIE_UC_SERVRITY_DLP 0x00000010
+#define PCIE_UC_SERVRITY_PSN_TLP 0x00001000
+#define PCIE_UC_SERVRITY_FCP 0x00002000
+#define PCIE_UC_SERVRITY_CPL_TO 0x00004000
+#define PCIE_UC_SERVRITY_CA 0x00008000
+#define PCIE_UC_SERVRITY_UC 0x00010000
+#define PCIE_UC_SERVRITY_ROV 0x00020000
+#define PCIE_UC_SERVRITY_MLFP 0x00040000
+#define PCIE_UC_SERVRITY_ECRC 0x00080000
+#define PCIE_UC_SERVRITY_UR 0x00100000
+
+#define REG_DEV_SERIALNUM_CTRL 0x200
+#define REG_DEV_MAC_SEL_MASK 0x0 /* 0:EUI; 1:MAC */
+#define REG_DEV_MAC_SEL_SHIFT 0
+#define REG_DEV_SERIAL_NUM_EN_MASK 0x1
+#define REG_DEV_SERIAL_NUM_EN_SHIFT 1
+
+#define REG_TWSI_CTRL 0x218
+#define TWSI_CTRL_LD_OFFSET_MASK 0xFF
+#define TWSI_CTRL_LD_OFFSET_SHIFT 0
+#define TWSI_CTRL_LD_SLV_ADDR_MASK 0x7
+#define TWSI_CTRL_LD_SLV_ADDR_SHIFT 8
+#define TWSI_CTRL_SW_LDSTART 0x800
+#define TWSI_CTRL_HW_LDSTART 0x1000
+#define TWSI_CTRL_SMB_SLV_ADDR_MASK 0x7F
+#define TWSI_CTRL_SMB_SLV_ADDR_SHIFT 15
+#define TWSI_CTRL_LD_EXIST 0x400000
+#define TWSI_CTRL_READ_FREQ_SEL_MASK 0x3
+#define TWSI_CTRL_READ_FREQ_SEL_SHIFT 23
+#define TWSI_CTRL_FREQ_SEL_100K 0
+#define TWSI_CTRL_FREQ_SEL_200K 1
+#define TWSI_CTRL_FREQ_SEL_300K 2
+#define TWSI_CTRL_FREQ_SEL_400K 3
+#define TWSI_CTRL_SMB_SLV_ADDR
+#define TWSI_CTRL_WRITE_FREQ_SEL_MASK 0x3
+#define TWSI_CTRL_WRITE_FREQ_SEL_SHIFT 24
+
+
+#define REG_PCIE_DEV_MISC_CTRL 0x21C
+#define PCIE_DEV_MISC_EXT_PIPE 0x2
+#define PCIE_DEV_MISC_RETRY_BUFDIS 0x1
+#define PCIE_DEV_MISC_SPIROM_EXIST 0x4
+#define PCIE_DEV_MISC_SERDES_ENDIAN 0x8
+#define PCIE_DEV_MISC_SERDES_SEL_DIN 0x10
+
+#define REG_PCIE_PHYMISC 0x1000
+#define PCIE_PHYMISC_FORCE_RCV_DET 0x4
+
+#define REG_PCIE_PHYMISC2 0x1004
+#define PCIE_PHYMISC2_SERDES_CDR_MASK 0x3
+#define PCIE_PHYMISC2_SERDES_CDR_SHIFT 16
+#define PCIE_PHYMISC2_SERDES_TH_MASK 0x3
+#define PCIE_PHYMISC2_SERDES_TH_SHIFT 18
+
+#define REG_TWSI_DEBUG 0x1108
+#define TWSI_DEBUG_DEV_EXIST 0x20000000
+
+#define REG_EEPROM_CTRL 0x12C0
+#define EEPROM_CTRL_DATA_HI_MASK 0xFFFF
+#define EEPROM_CTRL_DATA_HI_SHIFT 0
+#define EEPROM_CTRL_ADDR_MASK 0x3FF
+#define EEPROM_CTRL_ADDR_SHIFT 16
+#define EEPROM_CTRL_ACK 0x40000000
+#define EEPROM_CTRL_RW 0x80000000
+
+#define REG_EEPROM_DATA_LO 0x12C4
+
+#define REG_OTP_CTRL 0x12F0
+#define OTP_CTRL_CLK_EN 0x0002
+
+#define REG_PM_CTRL 0x12F8
+#define PM_CTRL_SDES_EN 0x00000001
+#define PM_CTRL_RBER_EN 0x00000002
+#define PM_CTRL_CLK_REQ_EN 0x00000004
+#define PM_CTRL_ASPM_L1_EN 0x00000008
+#define PM_CTRL_SERDES_L1_EN 0x00000010
+#define PM_CTRL_SERDES_PLL_L1_EN 0x00000020
+#define PM_CTRL_SERDES_PD_EX_L1 0x00000040
+#define PM_CTRL_SERDES_BUDS_RX_L1_EN 0x00000080
+#define PM_CTRL_L0S_ENTRY_TIMER_MASK 0xF
+#define PM_CTRL_L0S_ENTRY_TIMER_SHIFT 8
+#define PM_CTRL_ASPM_L0S_EN 0x00001000
+#define PM_CTRL_CLK_SWH_L1 0x00002000
+#define PM_CTRL_CLK_PWM_VER1_1 0x00004000
+#define PM_CTRL_RCVR_WT_TIMER 0x00008000
+#define PM_CTRL_L1_ENTRY_TIMER_MASK 0xF
+#define PM_CTRL_L1_ENTRY_TIMER_SHIFT 16
+#define PM_CTRL_PM_REQ_TIMER_MASK 0xF
+#define PM_CTRL_PM_REQ_TIMER_SHIFT 20
+#define PM_CTRL_LCKDET_TIMER_MASK 0xF
+#define PM_CTRL_LCKDET_TIMER_SHIFT 24
+#define PM_CTRL_EN_BUFS_RX_L0S 0x10000000
+#define PM_CTRL_SA_DLY_EN 0x20000000
+#define PM_CTRL_MAC_ASPM_CHK 0x40000000
+#define PM_CTRL_HOTRST 0x80000000
+
+#define REG_LTSSM_ID_CTRL 0x12FC
+#define LTSSM_ID_EN_WRO 0x1000
+/* Selene Master Control Register */
+#define REG_MASTER_CTRL 0x1400
+#define MASTER_CTRL_SOFT_RST 0x1
+#define MASTER_CTRL_TEST_MODE_MASK 0x3
+#define MASTER_CTRL_TEST_MODE_SHIFT 2
+#define MASTER_CTRL_BERT_START 0x10
+#define MASTER_CTRL_OOB_DIS_OFF 0x40
+#define MASTER_CTRL_SA_TIMER_EN 0x80
+#define MASTER_CTRL_MTIMER_EN 0x100
+#define MASTER_CTRL_MANUAL_INT 0x200
+#define MASTER_CTRL_TX_ITIMER_EN 0x400
+#define MASTER_CTRL_RX_ITIMER_EN 0x800
+#define MASTER_CTRL_CLK_SEL_DIS 0x1000
+#define MASTER_CTRL_CLK_SWH_MODE 0x2000
+#define MASTER_CTRL_INT_RDCLR 0x4000
+#define MASTER_CTRL_REV_NUM_SHIFT 16
+#define MASTER_CTRL_REV_NUM_MASK 0xff
+#define MASTER_CTRL_DEV_ID_SHIFT 24
+#define MASTER_CTRL_DEV_ID_MASK 0x7f
+#define MASTER_CTRL_OTP_SEL 0x80000000
+
+/* Timer Initial Value Register */
+#define REG_MANUAL_TIMER_INIT 0x1404
+
+/* IRQ ModeratorTimer Initial Value Register */
+#define REG_IRQ_MODRT_TIMER_INIT 0x1408
+#define IRQ_MODRT_TIMER_MASK 0xffff
+#define IRQ_MODRT_TX_TIMER_SHIFT 0
+#define IRQ_MODRT_RX_TIMER_SHIFT 16
+
+#define REG_GPHY_CTRL 0x140C
+#define GPHY_CTRL_EXT_RESET 0x1
+#define GPHY_CTRL_RTL_MODE 0x2
+#define GPHY_CTRL_LED_MODE 0x4
+#define GPHY_CTRL_ANEG_NOW 0x8
+#define GPHY_CTRL_REV_ANEG 0x10
+#define GPHY_CTRL_GATE_25M_EN 0x20
+#define GPHY_CTRL_LPW_EXIT 0x40
+#define GPHY_CTRL_PHY_IDDQ 0x80
+#define GPHY_CTRL_PHY_IDDQ_DIS 0x100
+#define GPHY_CTRL_GIGA_DIS 0x200
+#define GPHY_CTRL_HIB_EN 0x400
+#define GPHY_CTRL_HIB_PULSE 0x800
+#define GPHY_CTRL_SEL_ANA_RST 0x1000
+#define GPHY_CTRL_PHY_PLL_ON 0x2000
+#define GPHY_CTRL_PWDOWN_HW 0x4000
+#define GPHY_CTRL_PHY_PLL_BYPASS 0x8000
+
+#define GPHY_CTRL_DEFAULT ( \
+ GPHY_CTRL_SEL_ANA_RST |\
+ GPHY_CTRL_HIB_PULSE |\
+ GPHY_CTRL_HIB_EN)
+
+#define GPHY_CTRL_PW_WOL_DIS ( \
+ GPHY_CTRL_SEL_ANA_RST |\
+ GPHY_CTRL_HIB_PULSE |\
+ GPHY_CTRL_HIB_EN |\
+ GPHY_CTRL_PWDOWN_HW |\
+ GPHY_CTRL_PHY_IDDQ)
+
+#define GPHY_CTRL_POWER_SAVING ( \
+ GPHY_CTRL_SEL_ANA_RST |\
+ GPHY_CTRL_HIB_EN |\
+ GPHY_CTRL_HIB_PULSE |\
+ GPHY_CTRL_PWDOWN_HW |\
+ GPHY_CTRL_PHY_IDDQ)
+/* Block IDLE Status Register */
+#define REG_IDLE_STATUS 0x1410
+#define IDLE_STATUS_MASK 0x00FF
+#define IDLE_STATUS_RXMAC_NO_IDLE 0x1
+#define IDLE_STATUS_TXMAC_NO_IDLE 0x2
+#define IDLE_STATUS_RXQ_NO_IDLE 0x4
+#define IDLE_STATUS_TXQ_NO_IDLE 0x8
+#define IDLE_STATUS_DMAR_NO_IDLE 0x10
+#define IDLE_STATUS_DMAW_NO_IDLE 0x20
+#define IDLE_STATUS_SMB_NO_IDLE 0x40
+#define IDLE_STATUS_CMB_NO_IDLE 0x80
+
+/* MDIO Control Register */
+#define REG_MDIO_CTRL 0x1414
+#define MDIO_DATA_MASK 0xffff /* On MDIO write, the 16-bit
+ * control data to write to PHY
+ * MII management register */
+#define MDIO_DATA_SHIFT 0 /* On MDIO read, the 16-bit
+ * status data that was read
+ * from the PHY MII management register */
+#define MDIO_REG_ADDR_MASK 0x1f /* MDIO register address */
+#define MDIO_REG_ADDR_SHIFT 16
+#define MDIO_RW 0x200000 /* 1: read, 0: write */
+#define MDIO_SUP_PREAMBLE 0x400000 /* Suppress preamble */
+#define MDIO_START 0x800000 /* Write 1 to initiate the MDIO
+ * master. And this bit is self
+ * cleared after one cycle */
+#define MDIO_CLK_SEL_SHIFT 24
+#define MDIO_CLK_25_4 0
+#define MDIO_CLK_25_6 2
+#define MDIO_CLK_25_8 3
+#define MDIO_CLK_25_10 4
+#define MDIO_CLK_25_14 5
+#define MDIO_CLK_25_20 6
+#define MDIO_CLK_25_28 7
+#define MDIO_BUSY 0x8000000
+#define MDIO_AP_EN 0x10000000
+#define MDIO_WAIT_TIMES 10
+
+/* MII PHY Status Register */
+#define REG_PHY_STATUS 0x1418
+#define PHY_GENERAL_STATUS_MASK 0xFFFF
+#define PHY_STATUS_RECV_ENABLE 0x0001
+#define PHY_OE_PWSP_STATUS_MASK 0x07FF
+#define PHY_OE_PWSP_STATUS_SHIFT 16
+#define PHY_STATUS_LPW_STATE 0x80000000
+/* BIST Control and Status Register0 (for the Packet Memory) */
+#define REG_BIST0_CTRL 0x141c
+#define BIST0_NOW 0x1
+#define BIST0_SRAM_FAIL 0x2 /* 1: The SRAM failure is
+ * un-repairable because
+ * it has address decoder
+ * failure or more than 1 cell
+ * stuck-to-x failure */
+#define BIST0_FUSE_FLAG 0x4
+
+/* BIST Control and Status Register1(for the retry buffer of PCI Express) */
+#define REG_BIST1_CTRL 0x1420
+#define BIST1_NOW 0x1
+#define BIST1_SRAM_FAIL 0x2
+#define BIST1_FUSE_FLAG 0x4
+
+/* SerDes Lock Detect Control and Status Register */
+#define REG_SERDES_LOCK 0x1424
+#define SERDES_LOCK_DETECT 0x1 /* SerDes lock detected. This signal
+ * comes from Analog SerDes */
+#define SERDES_LOCK_DETECT_EN 0x2 /* 1: Enable SerDes Lock detect function */
+#define SERDES_LOCK_STS_SELFB_PLL_SHIFT 0xE
+#define SERDES_LOCK_STS_SELFB_PLL_MASK 0x3
+#define SERDES_OVCLK_18_25 0x0
+#define SERDES_OVCLK_12_18 0x1
+#define SERDES_OVCLK_0_4 0x2
+#define SERDES_OVCLK_4_12 0x3
+#define SERDES_MAC_CLK_SLOWDOWN 0x20000
+#define SERDES_PYH_CLK_SLOWDOWN 0x40000
+
+/* MAC Control Register */
+#define REG_MAC_CTRL 0x1480
+#define MAC_CTRL_TX_EN 0x1
+#define MAC_CTRL_RX_EN 0x2
+#define MAC_CTRL_TX_FLOW 0x4
+#define MAC_CTRL_RX_FLOW 0x8
+#define MAC_CTRL_LOOPBACK 0x10
+#define MAC_CTRL_DUPLX 0x20
+#define MAC_CTRL_ADD_CRC 0x40
+#define MAC_CTRL_PAD 0x80
+#define MAC_CTRL_LENCHK 0x100
+#define MAC_CTRL_HUGE_EN 0x200
+#define MAC_CTRL_PRMLEN_SHIFT 10
+#define MAC_CTRL_PRMLEN_MASK 0xf
+#define MAC_CTRL_RMV_VLAN 0x4000
+#define MAC_CTRL_PROMIS_EN 0x8000
+#define MAC_CTRL_TX_PAUSE 0x10000
+#define MAC_CTRL_SCNT 0x20000
+#define MAC_CTRL_SRST_TX 0x40000
+#define MAC_CTRL_TX_SIMURST 0x80000
+#define MAC_CTRL_SPEED_SHIFT 20
+#define MAC_CTRL_SPEED_MASK 0x3
+#define MAC_CTRL_DBG_TX_BKPRESURE 0x400000
+#define MAC_CTRL_TX_HUGE 0x800000
+#define MAC_CTRL_RX_CHKSUM_EN 0x1000000
+#define MAC_CTRL_MC_ALL_EN 0x2000000
+#define MAC_CTRL_BC_EN 0x4000000
+#define MAC_CTRL_DBG 0x8000000
+#define MAC_CTRL_SINGLE_PAUSE_EN 0x10000000
+#define MAC_CTRL_HASH_ALG_CRC32 0x20000000
+#define MAC_CTRL_SPEED_MODE_SW 0x40000000
+
+/* MAC IPG/IFG Control Register */
+#define REG_MAC_IPG_IFG 0x1484
+#define MAC_IPG_IFG_IPGT_SHIFT 0 /* Desired back to back
+ * inter-packet gap. The
+ * default is 96-bit time */
+#define MAC_IPG_IFG_IPGT_MASK 0x7f
+#define MAC_IPG_IFG_MIFG_SHIFT 8 /* Minimum number of IFG to
+ * enforce in between RX frames */
+#define MAC_IPG_IFG_MIFG_MASK 0xff /* Frame gap below such IFP is dropped */
+#define MAC_IPG_IFG_IPGR1_SHIFT 16 /* 64bit Carrier-Sense window */
+#define MAC_IPG_IFG_IPGR1_MASK 0x7f
+#define MAC_IPG_IFG_IPGR2_SHIFT 24 /* 96-bit IPG window */
+#define MAC_IPG_IFG_IPGR2_MASK 0x7f
+
+/* MAC STATION ADDRESS */
+#define REG_MAC_STA_ADDR 0x1488
+
+/* Hash table for multicast address */
+#define REG_RX_HASH_TABLE 0x1490
+
+/* MAC Half-Duplex Control Register */
+#define REG_MAC_HALF_DUPLX_CTRL 0x1498
+#define MAC_HALF_DUPLX_CTRL_LCOL_SHIFT 0 /* Collision Window */
+#define MAC_HALF_DUPLX_CTRL_LCOL_MASK 0x3ff
+#define MAC_HALF_DUPLX_CTRL_RETRY_SHIFT 12
+#define MAC_HALF_DUPLX_CTRL_RETRY_MASK 0xf
+#define MAC_HALF_DUPLX_CTRL_EXC_DEF_EN 0x10000
+#define MAC_HALF_DUPLX_CTRL_NO_BACK_C 0x20000
+#define MAC_HALF_DUPLX_CTRL_NO_BACK_P 0x40000 /* No back-off on backpressure,
+ * immediately start the
+ * transmission after back pressure */
+#define MAC_HALF_DUPLX_CTRL_ABEBE 0x80000 /* 1: Alternative Binary Exponential Back-off Enabled */
+#define MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT 20 /* Maximum binary exponential number */
+#define MAC_HALF_DUPLX_CTRL_ABEBT_MASK 0xf
+#define MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT 24 /* IPG to start JAM for collision based flow control in half-duplex */
+#define MAC_HALF_DUPLX_CTRL_JAMIPG_MASK 0xf /* mode. In unit of 8-bit time */
+
+/* Maximum Frame Length Control Register */
+#define REG_MTU 0x149c
+
+/* Wake-On-Lan control register */
+#define REG_WOL_CTRL 0x14a0
+#define WOL_PATTERN_EN 0x00000001
+#define WOL_PATTERN_PME_EN 0x00000002
+#define WOL_MAGIC_EN 0x00000004
+#define WOL_MAGIC_PME_EN 0x00000008
+#define WOL_LINK_CHG_EN 0x00000010
+#define WOL_LINK_CHG_PME_EN 0x00000020
+#define WOL_PATTERN_ST 0x00000100
+#define WOL_MAGIC_ST 0x00000200
+#define WOL_LINKCHG_ST 0x00000400
+#define WOL_CLK_SWITCH_EN 0x00008000
+#define WOL_PT0_EN 0x00010000
+#define WOL_PT1_EN 0x00020000
+#define WOL_PT2_EN 0x00040000
+#define WOL_PT3_EN 0x00080000
+#define WOL_PT4_EN 0x00100000
+#define WOL_PT5_EN 0x00200000
+#define WOL_PT6_EN 0x00400000
+
+/* WOL Length ( 2 DWORD ) */
+#define REG_WOL_PATTERN_LEN 0x14a4
+#define WOL_PT_LEN_MASK 0x7f
+#define WOL_PT0_LEN_SHIFT 0
+#define WOL_PT1_LEN_SHIFT 8
+#define WOL_PT2_LEN_SHIFT 16
+#define WOL_PT3_LEN_SHIFT 24
+#define WOL_PT4_LEN_SHIFT 0
+#define WOL_PT5_LEN_SHIFT 8
+#define WOL_PT6_LEN_SHIFT 16
+
+/* Internal SRAM Partition Register */
+#define RFDX_HEAD_ADDR_MASK 0x03FF
+#define RFDX_HARD_ADDR_SHIFT 0
+#define RFDX_TAIL_ADDR_MASK 0x03FF
+#define RFDX_TAIL_ADDR_SHIFT 16
+
+#define REG_SRAM_RFD0_INFO 0x1500
+#define REG_SRAM_RFD1_INFO 0x1504
+#define REG_SRAM_RFD2_INFO 0x1508
+#define REG_SRAM_RFD3_INFO 0x150C
+
+#define REG_RFD_NIC_LEN 0x1510 /* In 8-bytes */
+#define RFD_NIC_LEN_MASK 0x03FF
+
+#define REG_SRAM_TRD_ADDR 0x1518
+#define TPD_HEAD_ADDR_MASK 0x03FF
+#define TPD_HEAD_ADDR_SHIFT 0
+#define TPD_TAIL_ADDR_MASK 0x03FF
+#define TPD_TAIL_ADDR_SHIFT 16
+
+#define REG_SRAM_TRD_LEN 0x151C /* In 8-bytes */
+#define TPD_NIC_LEN_MASK 0x03FF
+
+#define REG_SRAM_RXF_ADDR 0x1520
+#define REG_SRAM_RXF_LEN 0x1524
+#define REG_SRAM_TXF_ADDR 0x1528
+#define REG_SRAM_TXF_LEN 0x152C
+#define REG_SRAM_TCPH_ADDR 0x1530
+#define REG_SRAM_PKTH_ADDR 0x1532
+
+/*
+ * Load Ptr Register
+ * Software sets this bit after the initialization of the head and tail */
+#define REG_LOAD_PTR 0x1534
+
+/*
+ * addresses of all descriptors, as well as the following descriptor
+ * control register, which triggers each function block to load the head
+ * pointer to prepare for the operation. This bit is then self-cleared
+ * after one cycle.
+ */
+#define REG_RX_BASE_ADDR_HI 0x1540
+#define REG_TX_BASE_ADDR_HI 0x1544
+#define REG_SMB_BASE_ADDR_HI 0x1548
+#define REG_SMB_BASE_ADDR_LO 0x154C
+#define REG_RFD0_HEAD_ADDR_LO 0x1550
+#define REG_RFD1_HEAD_ADDR_LO 0x1554
+#define REG_RFD2_HEAD_ADDR_LO 0x1558
+#define REG_RFD3_HEAD_ADDR_LO 0x155C
+#define REG_RFD_RING_SIZE 0x1560
+#define RFD_RING_SIZE_MASK 0x0FFF
+#define REG_RX_BUF_SIZE 0x1564
+#define RX_BUF_SIZE_MASK 0xFFFF
+#define REG_RRD0_HEAD_ADDR_LO 0x1568
+#define REG_RRD1_HEAD_ADDR_LO 0x156C
+#define REG_RRD2_HEAD_ADDR_LO 0x1570
+#define REG_RRD3_HEAD_ADDR_LO 0x1574
+#define REG_RRD_RING_SIZE 0x1578
+#define RRD_RING_SIZE_MASK 0x0FFF
+#define REG_HTPD_HEAD_ADDR_LO 0x157C
+#define REG_NTPD_HEAD_ADDR_LO 0x1580
+#define REG_TPD_RING_SIZE 0x1584
+#define TPD_RING_SIZE_MASK 0xFFFF
+#define REG_CMB_BASE_ADDR_LO 0x1588
+
+/* RSS about */
+#define REG_RSS_KEY0 0x14B0
+#define REG_RSS_KEY1 0x14B4
+#define REG_RSS_KEY2 0x14B8
+#define REG_RSS_KEY3 0x14BC
+#define REG_RSS_KEY4 0x14C0
+#define REG_RSS_KEY5 0x14C4
+#define REG_RSS_KEY6 0x14C8
+#define REG_RSS_KEY7 0x14CC
+#define REG_RSS_KEY8 0x14D0
+#define REG_RSS_KEY9 0x14D4
+#define REG_IDT_TABLE0 0x14E0
+#define REG_IDT_TABLE1 0x14E4
+#define REG_IDT_TABLE2 0x14E8
+#define REG_IDT_TABLE3 0x14EC
+#define REG_IDT_TABLE4 0x14F0
+#define REG_IDT_TABLE5 0x14F4
+#define REG_IDT_TABLE6 0x14F8
+#define REG_IDT_TABLE7 0x14FC
+#define REG_IDT_TABLE REG_IDT_TABLE0
+#define REG_RSS_HASH_VALUE 0x15B0
+#define REG_RSS_HASH_FLAG 0x15B4
+#define REG_BASE_CPU_NUMBER 0x15B8
+
+/* TXQ Control Register */
+#define REG_TXQ_CTRL 0x1590
+#define TXQ_NUM_TPD_BURST_MASK 0xF
+#define TXQ_NUM_TPD_BURST_SHIFT 0
+#define TXQ_CTRL_IP_OPTION_EN 0x10
+#define TXQ_CTRL_EN 0x20
+#define TXQ_CTRL_ENH_MODE 0x40
+#define TXQ_CTRL_LS_8023_EN 0x80
+#define TXQ_TXF_BURST_NUM_SHIFT 16
+#define TXQ_TXF_BURST_NUM_MASK 0xFFFF
+
+/* Jumbo packet Threshold for task offload */
+#define REG_TX_TSO_OFFLOAD_THRESH 0x1594 /* In 8-bytes */
+#define TX_TSO_OFFLOAD_THRESH_MASK 0x07FF
+
+#define REG_TXF_WATER_MARK 0x1598 /* In 8-bytes */
+#define TXF_WATER_MARK_MASK 0x0FFF
+#define TXF_LOW_WATER_MARK_SHIFT 0
+#define TXF_HIGH_WATER_MARK_SHIFT 16
+#define TXQ_CTRL_BURST_MODE_EN 0x80000000
+
+#define REG_THRUPUT_MON_CTRL 0x159C
+#define THRUPUT_MON_RATE_MASK 0x3
+#define THRUPUT_MON_RATE_SHIFT 0
+#define THRUPUT_MON_EN 0x80
+
+/* RXQ Control Register */
+#define REG_RXQ_CTRL 0x15A0
+#define ASPM_THRUPUT_LIMIT_MASK 0x3
+#define ASPM_THRUPUT_LIMIT_SHIFT 0
+#define ASPM_THRUPUT_LIMIT_NO 0x00
+#define ASPM_THRUPUT_LIMIT_1M 0x01
+#define ASPM_THRUPUT_LIMIT_10M 0x02
+#define ASPM_THRUPUT_LIMIT_100M 0x04
+#define RXQ1_CTRL_EN 0x10
+#define RXQ2_CTRL_EN 0x20
+#define RXQ3_CTRL_EN 0x40
+#define IPV6_CHKSUM_CTRL_EN 0x80
+#define RSS_HASH_BITS_MASK 0x00FF
+#define RSS_HASH_BITS_SHIFT 8
+#define RSS_HASH_IPV4 0x10000
+#define RSS_HASH_IPV4_TCP 0x20000
+#define RSS_HASH_IPV6 0x40000
+#define RSS_HASH_IPV6_TCP 0x80000
+#define RXQ_RFD_BURST_NUM_MASK 0x003F
+#define RXQ_RFD_BURST_NUM_SHIFT 20
+#define RSS_MODE_MASK 0x0003
+#define RSS_MODE_SHIFT 26
+#define RSS_NIP_QUEUE_SEL_MASK 0x1
+#define RSS_NIP_QUEUE_SEL_SHIFT 28
+#define RRS_HASH_CTRL_EN 0x20000000
+#define RX_CUT_THRU_EN 0x40000000
+#define RXQ_CTRL_EN 0x80000000
+
+#define REG_RFD_FREE_THRESH 0x15A4
+#define RFD_FREE_THRESH_MASK 0x003F
+#define RFD_FREE_HI_THRESH_SHIFT 0
+#define RFD_FREE_LO_THRESH_SHIFT 6
+
+/* RXF flow control register */
+#define REG_RXQ_RXF_PAUSE_THRESH 0x15A8
+#define RXQ_RXF_PAUSE_TH_HI_SHIFT 0
+#define RXQ_RXF_PAUSE_TH_HI_MASK 0x0FFF
+#define RXQ_RXF_PAUSE_TH_LO_SHIFT 16
+#define RXQ_RXF_PAUSE_TH_LO_MASK 0x0FFF
+
+#define REG_RXD_DMA_CTRL 0x15AC
+#define RXD_DMA_THRESH_MASK 0x0FFF /* In 8-bytes */
+#define RXD_DMA_THRESH_SHIFT 0
+#define RXD_DMA_DOWN_TIMER_MASK 0xFFFF
+#define RXD_DMA_DOWN_TIMER_SHIFT 16
+
+/* DMA Engine Control Register */
+#define REG_DMA_CTRL 0x15C0
+#define DMA_CTRL_DMAR_IN_ORDER 0x1
+#define DMA_CTRL_DMAR_ENH_ORDER 0x2
+#define DMA_CTRL_DMAR_OUT_ORDER 0x4
+#define DMA_CTRL_RCB_VALUE 0x8
+#define DMA_CTRL_DMAR_BURST_LEN_MASK 0x0007
+#define DMA_CTRL_DMAR_BURST_LEN_SHIFT 4
+#define DMA_CTRL_DMAW_BURST_LEN_MASK 0x0007
+#define DMA_CTRL_DMAW_BURST_LEN_SHIFT 7
+#define DMA_CTRL_DMAR_REQ_PRI 0x400
+#define DMA_CTRL_DMAR_DLY_CNT_MASK 0x001F
+#define DMA_CTRL_DMAR_DLY_CNT_SHIFT 11
+#define DMA_CTRL_DMAW_DLY_CNT_MASK 0x000F
+#define DMA_CTRL_DMAW_DLY_CNT_SHIFT 16
+#define DMA_CTRL_CMB_EN 0x100000
+#define DMA_CTRL_SMB_EN 0x200000
+#define DMA_CTRL_CMB_NOW 0x400000
+#define MAC_CTRL_SMB_DIS 0x1000000
+#define DMA_CTRL_SMB_NOW 0x80000000
+
+/* CMB/SMB Control Register */
+#define REG_SMB_STAT_TIMER 0x15C4 /* 2us resolution */
+#define SMB_STAT_TIMER_MASK 0xFFFFFF
+#define REG_CMB_TPD_THRESH 0x15C8
+#define CMB_TPD_THRESH_MASK 0xFFFF
+#define REG_CMB_TX_TIMER 0x15CC /* 2us resolution */
+#define CMB_TX_TIMER_MASK 0xFFFF
+
+/* Mail box */
+#define MB_RFDX_PROD_IDX_MASK 0xFFFF
+#define REG_MB_RFD0_PROD_IDX 0x15E0
+#define REG_MB_RFD1_PROD_IDX 0x15E4
+#define REG_MB_RFD2_PROD_IDX 0x15E8
+#define REG_MB_RFD3_PROD_IDX 0x15EC
+
+#define MB_PRIO_PROD_IDX_MASK 0xFFFF
+#define REG_MB_PRIO_PROD_IDX 0x15F0
+#define MB_HTPD_PROD_IDX_SHIFT 0
+#define MB_NTPD_PROD_IDX_SHIFT 16
+
+#define MB_PRIO_CONS_IDX_MASK 0xFFFF
+#define REG_MB_PRIO_CONS_IDX 0x15F4
+#define MB_HTPD_CONS_IDX_SHIFT 0
+#define MB_NTPD_CONS_IDX_SHIFT 16
+
+#define REG_MB_RFD01_CONS_IDX 0x15F8
+#define MB_RFD0_CONS_IDX_MASK 0x0000FFFF
+#define MB_RFD1_CONS_IDX_MASK 0xFFFF0000
+#define REG_MB_RFD23_CONS_IDX 0x15FC
+#define MB_RFD2_CONS_IDX_MASK 0x0000FFFF
+#define MB_RFD3_CONS_IDX_MASK 0xFFFF0000
+
+/* Interrupt Status Register */
+#define REG_ISR 0x1600
+#define ISR_SMB 0x00000001
+#define ISR_TIMER 0x00000002
+/*
+ * Software manual interrupt, for debug. Set when SW_MAN_INT_EN is set
+ * in Table 51 Selene Master Control Register (Offset 0x1400).
+ */
+#define ISR_MANUAL 0x00000004
+#define ISR_HW_RXF_OV 0x00000008 /* RXF overflow interrupt */
+#define ISR_RFD0_UR 0x00000010 /* RFD0 under run */
+#define ISR_RFD1_UR 0x00000020
+#define ISR_RFD2_UR 0x00000040
+#define ISR_RFD3_UR 0x00000080
+#define ISR_TXF_UR 0x00000100
+#define ISR_DMAR_TO_RST 0x00000200
+#define ISR_DMAW_TO_RST 0x00000400
+#define ISR_TX_CREDIT 0x00000800
+#define ISR_GPHY 0x00001000
+/* GPHY low power state interrupt */
+#define ISR_GPHY_LPW 0x00002000
+#define ISR_TXQ_TO_RST 0x00004000
+#define ISR_TX_PKT 0x00008000
+#define ISR_RX_PKT_0 0x00010000
+#define ISR_RX_PKT_1 0x00020000
+#define ISR_RX_PKT_2 0x00040000
+#define ISR_RX_PKT_3 0x00080000
+#define ISR_MAC_RX 0x00100000
+#define ISR_MAC_TX 0x00200000
+#define ISR_UR_DETECTED 0x00400000
+#define ISR_FERR_DETECTED 0x00800000
+#define ISR_NFERR_DETECTED 0x01000000
+#define ISR_CERR_DETECTED 0x02000000
+#define ISR_PHY_LINKDOWN 0x04000000
+#define ISR_DIS_INT 0x80000000
+
+/* Interrupt Mask Register */
+#define REG_IMR 0x1604
+
+#define IMR_NORMAL_MASK (\
+ ISR_MANUAL |\
+ ISR_HW_RXF_OV |\
+ ISR_RFD0_UR |\
+ ISR_TXF_UR |\
+ ISR_DMAR_TO_RST |\
+ ISR_TXQ_TO_RST |\
+ ISR_DMAW_TO_RST |\
+ ISR_GPHY |\
+ ISR_TX_PKT |\
+ ISR_RX_PKT_0 |\
+ ISR_GPHY_LPW |\
+ ISR_PHY_LINKDOWN)
+
+#define ISR_RX_PKT (\
+ ISR_RX_PKT_0 |\
+ ISR_RX_PKT_1 |\
+ ISR_RX_PKT_2 |\
+ ISR_RX_PKT_3)
+
+#define ISR_OVER (\
+ ISR_RFD0_UR |\
+ ISR_RFD1_UR |\
+ ISR_RFD2_UR |\
+ ISR_RFD3_UR |\
+ ISR_HW_RXF_OV |\
+ ISR_TXF_UR)
+
+#define ISR_ERROR (\
+ ISR_DMAR_TO_RST |\
+ ISR_TXQ_TO_RST |\
+ ISR_DMAW_TO_RST |\
+ ISR_PHY_LINKDOWN)
+
+#define REG_INT_RETRIG_TIMER 0x1608
+#define INT_RETRIG_TIMER_MASK 0xFFFF
+
+#define REG_HDS_CTRL 0x160C
+#define HDS_CTRL_EN 0x0001
+#define HDS_CTRL_BACKFILLSIZE_SHIFT 8
+#define HDS_CTRL_BACKFILLSIZE_MASK 0x0FFF
+#define HDS_CTRL_MAX_HDRSIZE_SHIFT 20
+#define HDS_CTRL_MAC_HDRSIZE_MASK 0x0FFF
+
+#define REG_MAC_RX_STATUS_BIN 0x1700
+#define REG_MAC_RX_STATUS_END 0x175c
+#define REG_MAC_TX_STATUS_BIN 0x1760
+#define REG_MAC_TX_STATUS_END 0x17c0
+
+#define REG_CLK_GATING_CTRL 0x1814
+#define CLK_GATING_DMAW_EN 0x0001
+#define CLK_GATING_DMAR_EN 0x0002
+#define CLK_GATING_TXQ_EN 0x0004
+#define CLK_GATING_RXQ_EN 0x0008
+#define CLK_GATING_TXMAC_EN 0x0010
+#define CLK_GATING_RXMAC_EN 0x0020
+
+#define CLK_GATING_EN_ALL (CLK_GATING_DMAW_EN |\
+ CLK_GATING_DMAR_EN |\
+ CLK_GATING_TXQ_EN |\
+ CLK_GATING_RXQ_EN |\
+ CLK_GATING_TXMAC_EN|\
+ CLK_GATING_RXMAC_EN)
+
+/* DEBUG ADDR */
+#define REG_DEBUG_DATA0 0x1900
+#define REG_DEBUG_DATA1 0x1904
+
+#define L1D_MPW_PHYID1 0xD01C /* V7 */
+#define L1D_MPW_PHYID2 0xD01D /* V1-V6 */
+#define L1D_MPW_PHYID3 0xD01E /* V8 */
+
+
+/* Autoneg Advertisement Register */
+#define ADVERTISE_DEFAULT_CAP \
+ (ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM)
+
+/* 1000BASE-T Control Register */
+#define GIGA_CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port 0=DTE device */
+
+#define GIGA_CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master 0=Configure PHY as Slave */
+#define GIGA_CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value 0=Automatic Master/Slave config */
+#define GIGA_CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */
+#define GIGA_CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */
+#define GIGA_CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */
+#define GIGA_CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */
+#define GIGA_CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */
+#define GIGA_CR_1000T_SPEED_MASK 0x0300
+#define GIGA_CR_1000T_DEFAULT_CAP 0x0300
+
+/* PHY Specific Status Register */
+#define MII_GIGA_PSSR 0x11
+#define GIGA_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */
+#define GIGA_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */
+#define GIGA_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */
+#define GIGA_PSSR_10MBS 0x0000 /* 00=10Mbs */
+#define GIGA_PSSR_100MBS 0x4000 /* 01=100Mbs */
+#define GIGA_PSSR_1000MBS 0x8000 /* 10=1000Mbs */
+
+/* PHY Interrupt Enable Register */
+#define MII_IER 0x12
+#define IER_LINK_UP 0x0400
+#define IER_LINK_DOWN 0x0800
+
+/* PHY Interrupt Status Register */
+#define MII_ISR 0x13
+#define ISR_LINK_UP 0x0400
+#define ISR_LINK_DOWN 0x0800
+
+/* Cable-Detect-Test Control Register */
+#define MII_CDTC 0x16
+#define CDTC_EN_OFF 0 /* sc */
+#define CDTC_EN_BITS 1
+#define CDTC_PAIR_OFF 8
+#define CDTC_PAIR_BIT 2
+
+/* Cable-Detect-Test Status Register */
+#define MII_CDTS 0x1C
+#define CDTS_STATUS_OFF 8
+#define CDTS_STATUS_BITS 2
+#define CDTS_STATUS_NORMAL 0
+#define CDTS_STATUS_SHORT 1
+#define CDTS_STATUS_OPEN 2
+#define CDTS_STATUS_INVALID 3
+
+#define MII_DBG_ADDR 0x1D
+#define MII_DBG_DATA 0x1E
+
+#define MII_ANA_CTRL_0 0x0
+#define ANA_RESTART_CAL 0x0001
+#define ANA_MANUL_SWICH_ON_SHIFT 0x1
+#define ANA_MANUL_SWICH_ON_MASK 0xF
+#define ANA_MAN_ENABLE 0x0020
+#define ANA_SEL_HSP 0x0040
+#define ANA_EN_HB 0x0080
+#define ANA_EN_HBIAS 0x0100
+#define ANA_OEN_125M 0x0200
+#define ANA_EN_LCKDT 0x0400
+#define ANA_LCKDT_PHY 0x0800
+#define ANA_AFE_MODE 0x1000
+#define ANA_VCO_SLOW 0x2000
+#define ANA_VCO_FAST 0x4000
+#define ANA_SEL_CLK125M_DSP 0x8000
+
+#define MII_ANA_CTRL_4 0x4
+#define ANA_IECHO_ADJ_MASK 0xF
+#define ANA_IECHO_ADJ_3_SHIFT 0
+#define ANA_IECHO_ADJ_2_SHIFT 4
+#define ANA_IECHO_ADJ_1_SHIFT 8
+#define ANA_IECHO_ADJ_0_SHIFT 12
+
+#define MII_ANA_CTRL_5 0x5
+#define ANA_SERDES_CDR_BW_SHIFT 0
+#define ANA_SERDES_CDR_BW_MASK 0x3
+#define ANA_MS_PAD_DBG 0x0004
+#define ANA_SPEEDUP_DBG 0x0008
+#define ANA_SERDES_TH_LOS_SHIFT 4
+#define ANA_SERDES_TH_LOS_MASK 0x3
+#define ANA_SERDES_EN_DEEM 0x0040
+#define ANA_SERDES_TXELECIDLE 0x0080
+#define ANA_SERDES_BEACON 0x0100
+#define ANA_SERDES_HALFTXDR 0x0200
+#define ANA_SERDES_SEL_HSP 0x0400
+#define ANA_SERDES_EN_PLL 0x0800
+#define ANA_SERDES_EN 0x1000
+#define ANA_SERDES_EN_LCKDT 0x2000
+
+#define MII_ANA_CTRL_11 0xB
+#define ANA_PS_HIB_EN 0x8000
+
+#define MII_ANA_CTRL_18 0x12
+#define ANA_TEST_MODE_10BT_01SHIFT 0
+#define ANA_TEST_MODE_10BT_01MASK 0x3
+#define ANA_LOOP_SEL_10BT 0x0004
+#define ANA_RGMII_MODE_SW 0x0008
+#define ANA_EN_LONGECABLE 0x0010
+#define ANA_TEST_MODE_10BT_2 0x0020
+#define ANA_EN_10BT_IDLE 0x0400
+#define ANA_EN_MASK_TB 0x0800
+#define ANA_TRIGGER_SEL_TIMER_SHIFT 12
+#define ANA_TRIGGER_SEL_TIMER_MASK 0x3
+#define ANA_INTERVAL_SEL_TIMER_SHIFT 14
+#define ANA_INTERVAL_SEL_TIMER_MASK 0x3
+
+#define MII_ANA_CTRL_41 0x29
+#define ANA_TOP_PS_EN 0x8000
+
+#define MII_ANA_CTRL_54 0x36
+#define ANA_LONG_CABLE_TH_100_SHIFT 0
+#define ANA_LONG_CABLE_TH_100_MASK 0x3F
+#define ANA_DESERVED 0x0040
+#define ANA_EN_LIT_CH 0x0080
+#define ANA_SHORT_CABLE_TH_100_SHIFT 8
+#define ANA_SHORT_CABLE_TH_100_MASK 0x3F
+#define ANA_BP_BAD_LINK_ACCUM 0x4000
+#define ANA_BP_SMALL_BW 0x8000
+
+#endif /*_ATL1C_HW_H_*/
diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
new file mode 100644
index 0000000..1381b03
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
@@ -0,0 +1,2969 @@
+/*
+ * Copyright(c) 2008 - 2009 Atheros Corporation. All rights reserved.
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ */
+
+#include "atl1c.h"
+
+#define ATL1C_DRV_VERSION "1.0.1.0-NAPI"
+char atl1c_driver_name[] = "atl1c";
+char atl1c_driver_version[] = ATL1C_DRV_VERSION;
+#define PCI_DEVICE_ID_ATTANSIC_L2C 0x1062
+#define PCI_DEVICE_ID_ATTANSIC_L1C 0x1063
+#define PCI_DEVICE_ID_ATHEROS_L2C_B 0x2060 /* AR8152 v1.1 Fast 10/100 */
+#define PCI_DEVICE_ID_ATHEROS_L2C_B2 0x2062 /* AR8152 v2.0 Fast 10/100 */
+#define PCI_DEVICE_ID_ATHEROS_L1D 0x1073 /* AR8151 v1.0 Gigabit 1000 */
+#define PCI_DEVICE_ID_ATHEROS_L1D_2_0 0x1083 /* AR8151 v2.0 Gigabit 1000 */
+#define L2CB_V10 0xc0
+#define L2CB_V11 0xc1
+
+/*
+ * atl1c_pci_tbl - PCI Device ID Table
+ *
+ * Wildcard entries (PCI_ANY_ID) should come last
+ * Last entry must be all 0s
+ *
+ * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
+ * Class, Class Mask, private data (not used) }
+ */
+static DEFINE_PCI_DEVICE_TABLE(atl1c_pci_tbl) = {
+ {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L1C)},
+ {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L2C)},
+ {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L2C_B)},
+ {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L2C_B2)},
+ {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L1D)},
+ {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L1D_2_0)},
+ /* required last entry */
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, atl1c_pci_tbl);
+
+MODULE_AUTHOR("Jie Yang <jie.yang@atheros.com>");
+MODULE_DESCRIPTION("Atheros 1000M Ethernet Network Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(ATL1C_DRV_VERSION);
+
+static int atl1c_stop_mac(struct atl1c_hw *hw);
+static void atl1c_enable_rx_ctrl(struct atl1c_hw *hw);
+static void atl1c_enable_tx_ctrl(struct atl1c_hw *hw);
+static void atl1c_disable_l0s_l1(struct atl1c_hw *hw);
+static void atl1c_set_aspm(struct atl1c_hw *hw, bool linkup);
+static void atl1c_setup_mac_ctrl(struct atl1c_adapter *adapter);
+static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, u8 que,
+ int *work_done, int work_to_do);
+static int atl1c_up(struct atl1c_adapter *adapter);
+static void atl1c_down(struct atl1c_adapter *adapter);
+
+static const u16 atl1c_pay_load_size[] = {
+ 128, 256, 512, 1024, 2048, 4096,
+};
+
+static const u16 atl1c_rfd_prod_idx_regs[AT_MAX_RECEIVE_QUEUE] =
+{
+ REG_MB_RFD0_PROD_IDX,
+ REG_MB_RFD1_PROD_IDX,
+ REG_MB_RFD2_PROD_IDX,
+ REG_MB_RFD3_PROD_IDX
+};
+
+static const u16 atl1c_rfd_addr_lo_regs[AT_MAX_RECEIVE_QUEUE] =
+{
+ REG_RFD0_HEAD_ADDR_LO,
+ REG_RFD1_HEAD_ADDR_LO,
+ REG_RFD2_HEAD_ADDR_LO,
+ REG_RFD3_HEAD_ADDR_LO
+};
+
+static const u16 atl1c_rrd_addr_lo_regs[AT_MAX_RECEIVE_QUEUE] =
+{
+ REG_RRD0_HEAD_ADDR_LO,
+ REG_RRD1_HEAD_ADDR_LO,
+ REG_RRD2_HEAD_ADDR_LO,
+ REG_RRD3_HEAD_ADDR_LO
+};
+
+static const u32 atl1c_default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE |
+ NETIF_MSG_LINK | NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP;
+static void atl1c_pcie_patch(struct atl1c_hw *hw)
+{
+ u32 data;
+
+ AT_READ_REG(hw, REG_PCIE_PHYMISC, &data);
+ data |= PCIE_PHYMISC_FORCE_RCV_DET;
+ AT_WRITE_REG(hw, REG_PCIE_PHYMISC, data);
+
+ if (hw->nic_type == athr_l2c_b && hw->revision_id == L2CB_V10) {
+ AT_READ_REG(hw, REG_PCIE_PHYMISC2, &data);
+
+ data &= ~(PCIE_PHYMISC2_SERDES_CDR_MASK <<
+ PCIE_PHYMISC2_SERDES_CDR_SHIFT);
+ data |= 3 << PCIE_PHYMISC2_SERDES_CDR_SHIFT;
+ data &= ~(PCIE_PHYMISC2_SERDES_TH_MASK <<
+ PCIE_PHYMISC2_SERDES_TH_SHIFT);
+ data |= 3 << PCIE_PHYMISC2_SERDES_TH_SHIFT;
+ AT_WRITE_REG(hw, REG_PCIE_PHYMISC2, data);
+ }
+}
+
+/* FIXME: no need any more ? */
+/*
+ * atl1c_init_pcie - init PCIE module
+ */
+static void atl1c_reset_pcie(struct atl1c_hw *hw, u32 flag)
+{
+ u32 data;
+ u32 pci_cmd;
+ struct pci_dev *pdev = hw->adapter->pdev;
+
+ AT_READ_REG(hw, PCI_COMMAND, &pci_cmd);
+ pci_cmd &= ~PCI_COMMAND_INTX_DISABLE;
+ pci_cmd |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+ PCI_COMMAND_IO);
+ AT_WRITE_REG(hw, PCI_COMMAND, pci_cmd);
+
+ /*
+ * Clear any PowerSaveing Settings
+ */
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ pci_enable_wake(pdev, PCI_D3cold, 0);
+
+ /*
+ * Mask some pcie error bits
+ */
+ AT_READ_REG(hw, REG_PCIE_UC_SEVERITY, &data);
+ data &= ~PCIE_UC_SERVRITY_DLP;
+ data &= ~PCIE_UC_SERVRITY_FCP;
+ AT_WRITE_REG(hw, REG_PCIE_UC_SEVERITY, data);
+
+ AT_READ_REG(hw, REG_LTSSM_ID_CTRL, &data);
+ data &= ~LTSSM_ID_EN_WRO;
+ AT_WRITE_REG(hw, REG_LTSSM_ID_CTRL, data);
+
+ atl1c_pcie_patch(hw);
+ if (flag & ATL1C_PCIE_L0S_L1_DISABLE)
+ atl1c_disable_l0s_l1(hw);
+ if (flag & ATL1C_PCIE_PHY_RESET)
+ AT_WRITE_REG(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT);
+ else
+ AT_WRITE_REG(hw, REG_GPHY_CTRL,
+ GPHY_CTRL_DEFAULT | GPHY_CTRL_EXT_RESET);
+
+ msleep(5);
+}
+
+/*
+ * atl1c_irq_enable - Enable default interrupt generation settings
+ * @adapter: board private structure
+ */
+static inline void atl1c_irq_enable(struct atl1c_adapter *adapter)
+{
+ if (likely(atomic_dec_and_test(&adapter->irq_sem))) {
+ AT_WRITE_REG(&adapter->hw, REG_ISR, 0x7FFFFFFF);
+ AT_WRITE_REG(&adapter->hw, REG_IMR, adapter->hw.intr_mask);
+ AT_WRITE_FLUSH(&adapter->hw);
+ }
+}
+
+/*
+ * atl1c_irq_disable - Mask off interrupt generation on the NIC
+ * @adapter: board private structure
+ */
+static inline void atl1c_irq_disable(struct atl1c_adapter *adapter)
+{
+ atomic_inc(&adapter->irq_sem);
+ AT_WRITE_REG(&adapter->hw, REG_IMR, 0);
+ AT_WRITE_REG(&adapter->hw, REG_ISR, ISR_DIS_INT);
+ AT_WRITE_FLUSH(&adapter->hw);
+ synchronize_irq(adapter->pdev->irq);
+}
+
+/*
+ * atl1c_irq_reset - reset interrupt confiure on the NIC
+ * @adapter: board private structure
+ */
+static inline void atl1c_irq_reset(struct atl1c_adapter *adapter)
+{
+ atomic_set(&adapter->irq_sem, 1);
+ atl1c_irq_enable(adapter);
+}
+
+/*
+ * atl1c_wait_until_idle - wait up to AT_HW_MAX_IDLE_DELAY reads
+ * of the idle status register until the device is actually idle
+ */
+static u32 atl1c_wait_until_idle(struct atl1c_hw *hw)
+{
+ int timeout;
+ u32 data;
+
+ for (timeout = 0; timeout < AT_HW_MAX_IDLE_DELAY; timeout++) {
+ AT_READ_REG(hw, REG_IDLE_STATUS, &data);
+ if ((data & IDLE_STATUS_MASK) == 0)
+ return 0;
+ msleep(1);
+ }
+ return data;
+}
+
+/*
+ * atl1c_phy_config - Timer Call-back
+ * @data: pointer to netdev cast into an unsigned long
+ */
+static void atl1c_phy_config(unsigned long data)
+{
+ struct atl1c_adapter *adapter = (struct atl1c_adapter *) data;
+ struct atl1c_hw *hw = &adapter->hw;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->mdio_lock, flags);
+ atl1c_restart_autoneg(hw);
+ spin_unlock_irqrestore(&adapter->mdio_lock, flags);
+}
+
+void atl1c_reinit_locked(struct atl1c_adapter *adapter)
+{
+ WARN_ON(in_interrupt());
+ atl1c_down(adapter);
+ atl1c_up(adapter);
+ clear_bit(__AT_RESETTING, &adapter->flags);
+}
+
+static void atl1c_check_link_status(struct atl1c_adapter *adapter)
+{
+ struct atl1c_hw *hw = &adapter->hw;
+ struct net_device *netdev = adapter->netdev;
+ struct pci_dev *pdev = adapter->pdev;
+ int err;
+ unsigned long flags;
+ u16 speed, duplex, phy_data;
+
+ spin_lock_irqsave(&adapter->mdio_lock, flags);
+ /* MII_BMSR must read twise */
+ atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
+ atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
+ spin_unlock_irqrestore(&adapter->mdio_lock, flags);
+
+ if ((phy_data & BMSR_LSTATUS) == 0) {
+ /* link down */
+ hw->hibernate = true;
+ if (atl1c_stop_mac(hw) != 0)
+ if (netif_msg_hw(adapter))
+ dev_warn(&pdev->dev, "stop mac failed\n");
+ atl1c_set_aspm(hw, false);
+ netif_carrier_off(netdev);
+ netif_stop_queue(netdev);
+ atl1c_phy_reset(hw);
+ atl1c_phy_init(&adapter->hw);
+ } else {
+ /* Link Up */
+ hw->hibernate = false;
+ spin_lock_irqsave(&adapter->mdio_lock, flags);
+ err = atl1c_get_speed_and_duplex(hw, &speed, &duplex);
+ spin_unlock_irqrestore(&adapter->mdio_lock, flags);
+ if (unlikely(err))
+ return;
+ /* link result is our setting */
+ if (adapter->link_speed != speed ||
+ adapter->link_duplex != duplex) {
+ adapter->link_speed = speed;
+ adapter->link_duplex = duplex;
+ atl1c_set_aspm(hw, true);
+ atl1c_enable_tx_ctrl(hw);
+ atl1c_enable_rx_ctrl(hw);
+ atl1c_setup_mac_ctrl(adapter);
+ if (netif_msg_link(adapter))
+ dev_info(&pdev->dev,
+ "%s: %s NIC Link is Up<%d Mbps %s>\n",
+ atl1c_driver_name, netdev->name,
+ adapter->link_speed,
+ adapter->link_duplex == FULL_DUPLEX ?
+ "Full Duplex" : "Half Duplex");
+ }
+ if (!netif_carrier_ok(netdev))
+ netif_carrier_on(netdev);
+ }
+}
+
+static void atl1c_link_chg_event(struct atl1c_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ struct pci_dev *pdev = adapter->pdev;
+ u16 phy_data;
+ u16 link_up;
+
+ spin_lock(&adapter->mdio_lock);
+ atl1c_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
+ atl1c_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
+ spin_unlock(&adapter->mdio_lock);
+ link_up = phy_data & BMSR_LSTATUS;
+ /* notify upper layer link down ASAP */
+ if (!link_up) {
+ if (netif_carrier_ok(netdev)) {
+ /* old link state: Up */
+ netif_carrier_off(netdev);
+ if (netif_msg_link(adapter))
+ dev_info(&pdev->dev,
+ "%s: %s NIC Link is Down\n",
+ atl1c_driver_name, netdev->name);
+ adapter->link_speed = SPEED_0;
+ }
+ }
+
+ set_bit(ATL1C_WORK_EVENT_LINK_CHANGE, &adapter->work_event);
+ schedule_work(&adapter->common_task);
+}
+
+static void atl1c_common_task(struct work_struct *work)
+{
+ struct atl1c_adapter *adapter;
+ struct net_device *netdev;
+
+ adapter = container_of(work, struct atl1c_adapter, common_task);
+ netdev = adapter->netdev;
+
+ if (test_and_clear_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event)) {
+ netif_device_detach(netdev);
+ atl1c_down(adapter);
+ atl1c_up(adapter);
+ netif_device_attach(netdev);
+ }
+
+ if (test_and_clear_bit(ATL1C_WORK_EVENT_LINK_CHANGE,
+ &adapter->work_event))
+ atl1c_check_link_status(adapter);
+}
+
+
+static void atl1c_del_timer(struct atl1c_adapter *adapter)
+{
+ del_timer_sync(&adapter->phy_config_timer);
+}
+
+
+/*
+ * atl1c_tx_timeout - Respond to a Tx Hang
+ * @netdev: network interface device structure
+ */
+static void atl1c_tx_timeout(struct net_device *netdev)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+
+ /* Do the reset outside of interrupt context */
+ set_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event);
+ schedule_work(&adapter->common_task);
+}
+
+/*
+ * atl1c_set_multi - Multicast and Promiscuous mode set
+ * @netdev: network interface device structure
+ *
+ * The set_multi entry point is called whenever the multicast address
+ * list or the network interface flags are updated. This routine is
+ * responsible for configuring the hardware for proper multicast,
+ * promiscuous mode, and all-multi behavior.
+ */
+static void atl1c_set_multi(struct net_device *netdev)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ struct atl1c_hw *hw = &adapter->hw;
+ struct netdev_hw_addr *ha;
+ u32 mac_ctrl_data;
+ u32 hash_value;
+
+ /* Check for Promiscuous and All Multicast modes */
+ AT_READ_REG(hw, REG_MAC_CTRL, &mac_ctrl_data);
+
+ if (netdev->flags & IFF_PROMISC) {
+ mac_ctrl_data |= MAC_CTRL_PROMIS_EN;
+ } else if (netdev->flags & IFF_ALLMULTI) {
+ mac_ctrl_data |= MAC_CTRL_MC_ALL_EN;
+ mac_ctrl_data &= ~MAC_CTRL_PROMIS_EN;
+ } else {
+ mac_ctrl_data &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN);
+ }
+
+ AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data);
+
+ /* clear the old settings from the multicast hash table */
+ AT_WRITE_REG(hw, REG_RX_HASH_TABLE, 0);
+ AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0);
+
+ /* comoute mc addresses' hash value ,and put it into hash table */
+ netdev_for_each_mc_addr(ha, netdev) {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ hash_value = atl1c_hash_mc_addr(hw, ha->addr);
+#else
+ hash_value = atl1c_hash_mc_addr(hw, ha->dmi_addr);
+#endif
+ atl1c_hash_set(hw, hash_value);
+ }
+}
+
+static void __atl1c_vlan_mode(u32 features, u32 *mac_ctrl_data)
+{
+ if (features & NETIF_F_HW_VLAN_RX) {
+ /* enable VLAN tag insert/strip */
+ *mac_ctrl_data |= MAC_CTRL_RMV_VLAN;
+ } else {
+ /* disable VLAN tag insert/strip */
+ *mac_ctrl_data &= ~MAC_CTRL_RMV_VLAN;
+ }
+}
+
+static void atl1c_vlan_mode(struct net_device *netdev, u32 features)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ struct pci_dev *pdev = adapter->pdev;
+ u32 mac_ctrl_data = 0;
+
+ if (netif_msg_pktdata(adapter))
+ dev_dbg(&pdev->dev, "atl1c_vlan_mode\n");
+
+ atl1c_irq_disable(adapter);
+ AT_READ_REG(&adapter->hw, REG_MAC_CTRL, &mac_ctrl_data);
+ __atl1c_vlan_mode(features, &mac_ctrl_data);
+ AT_WRITE_REG(&adapter->hw, REG_MAC_CTRL, mac_ctrl_data);
+ atl1c_irq_enable(adapter);
+}
+
+static void atl1c_restore_vlan(struct atl1c_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+
+ if (netif_msg_pktdata(adapter))
+ dev_dbg(&pdev->dev, "atl1c_restore_vlan\n");
+ atl1c_vlan_mode(adapter->netdev, adapter->netdev->features);
+}
+
+/*
+ * atl1c_set_mac - Change the Ethernet Address of the NIC
+ * @netdev: network interface device structure
+ * @p: pointer to an address structure
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int atl1c_set_mac_addr(struct net_device *netdev, void *p)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ struct sockaddr *addr = p;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ if (netif_running(netdev))
+ return -EBUSY;
+
+ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+ memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len);
+
+ atl1c_hw_set_mac_addr(&adapter->hw);
+
+ return 0;
+}
+
+static void atl1c_set_rxbufsize(struct atl1c_adapter *adapter,
+ struct net_device *dev)
+{
+ int mtu = dev->mtu;
+
+ adapter->rx_buffer_len = mtu > AT_RX_BUF_SIZE ?
+ roundup(mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN, 8) : AT_RX_BUF_SIZE;
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39))
+static u32 atl1c_fix_features(struct net_device *netdev, u32 features)
+{
+ /*
+ * Since there is no support for separate rx/tx vlan accel
+ * enable/disable make sure tx flag is always in same state as rx.
+ */
+ if (features & NETIF_F_HW_VLAN_RX)
+ features |= NETIF_F_HW_VLAN_TX;
+ else
+ features &= ~NETIF_F_HW_VLAN_TX;
+
+ if (netdev->mtu > MAX_TSO_FRAME_SIZE)
+ features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
+
+ return features;
+}
+
+static int atl1c_set_features(struct net_device *netdev, u32 features)
+{
+ u32 changed = netdev->features ^ features;
+
+ if (changed & NETIF_F_HW_VLAN_RX)
+ atl1c_vlan_mode(netdev, features);
+
+ return 0;
+}
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */
+
+/*
+ * atl1c_change_mtu - Change the Maximum Transfer Unit
+ * @netdev: network interface device structure
+ * @new_mtu: new value for maximum frame size
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int atl1c_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ int old_mtu = netdev->mtu;
+ int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+
+ if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) ||
+ (max_frame > MAX_JUMBO_FRAME_SIZE)) {
+ if (netif_msg_link(adapter))
+ dev_warn(&adapter->pdev->dev, "invalid MTU setting\n");
+ return -EINVAL;
+ }
+ /* set MTU */
+ if (old_mtu != new_mtu && netif_running(netdev)) {
+ while (test_and_set_bit(__AT_RESETTING, &adapter->flags))
+ msleep(1);
+ netdev->mtu = new_mtu;
+ adapter->hw.max_frame_size = new_mtu;
+ atl1c_set_rxbufsize(adapter, netdev);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39))
+ if (new_mtu > MAX_TSO_FRAME_SIZE) {
+ adapter->netdev->features &= ~NETIF_F_TSO;
+ adapter->netdev->features &= ~NETIF_F_TSO6;
+ } else {
+ adapter->netdev->features |= NETIF_F_TSO;
+ adapter->netdev->features |= NETIF_F_TSO6;
+ }
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */
+ atl1c_down(adapter);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39))
+ netdev_update_features(netdev);
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */
+ atl1c_up(adapter);
+ clear_bit(__AT_RESETTING, &adapter->flags);
+ if (adapter->hw.ctrl_flags & ATL1C_FPGA_VERSION) {
+ u32 phy_data;
+
+ AT_READ_REG(&adapter->hw, 0x1414, &phy_data);
+ phy_data |= 0x10000000;
+ AT_WRITE_REG(&adapter->hw, 0x1414, phy_data);
+ }
+
+ }
+ return 0;
+}
+
+/*
+ * caller should hold mdio_lock
+ */
+static int atl1c_mdio_read(struct net_device *netdev, int phy_id, int reg_num)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ u16 result;
+
+ atl1c_read_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, &result);
+ return result;
+}
+
+static void atl1c_mdio_write(struct net_device *netdev, int phy_id,
+ int reg_num, int val)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+
+ atl1c_write_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, val);
+}
+
+/*
+ * atl1c_mii_ioctl -
+ * @netdev:
+ * @ifreq:
+ * @cmd:
+ */
+static int atl1c_mii_ioctl(struct net_device *netdev,
+ struct ifreq *ifr, int cmd)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ struct pci_dev *pdev = adapter->pdev;
+ struct mii_ioctl_data *data = if_mii(ifr);
+ unsigned long flags;
+ int retval = 0;
+
+ if (!netif_running(netdev))
+ return -EINVAL;
+
+ spin_lock_irqsave(&adapter->mdio_lock, flags);
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ data->phy_id = 0;
+ break;
+
+ case SIOCGMIIREG:
+ if (atl1c_read_phy_reg(&adapter->hw, data->reg_num & 0x1F,
+ &data->val_out)) {
+ retval = -EIO;
+ goto out;
+ }
+ break;
+
+ case SIOCSMIIREG:
+ if (data->reg_num & ~(0x1F)) {
+ retval = -EFAULT;
+ goto out;
+ }
+
+ dev_dbg(&pdev->dev, "<atl1c_mii_ioctl> write %x %x",
+ data->reg_num, data->val_in);
+ if (atl1c_write_phy_reg(&adapter->hw,
+ data->reg_num, data->val_in)) {
+ retval = -EIO;
+ goto out;
+ }
+ break;
+
+ default:
+ retval = -EOPNOTSUPP;
+ break;
+ }
+out:
+ spin_unlock_irqrestore(&adapter->mdio_lock, flags);
+ return retval;
+}
+
+/*
+ * atl1c_ioctl -
+ * @netdev:
+ * @ifreq:
+ * @cmd:
+ */
+static int atl1c_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ case SIOCGMIIREG:
+ case SIOCSMIIREG:
+ return atl1c_mii_ioctl(netdev, ifr, cmd);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/*
+ * atl1c_alloc_queues - Allocate memory for all rings
+ * @adapter: board private structure to initialize
+ *
+ */
+static int __devinit atl1c_alloc_queues(struct atl1c_adapter *adapter)
+{
+ return 0;
+}
+
+static void atl1c_set_mac_type(struct atl1c_hw *hw)
+{
+ switch (hw->device_id) {
+ case PCI_DEVICE_ID_ATTANSIC_L2C:
+ hw->nic_type = athr_l2c;
+ break;
+ case PCI_DEVICE_ID_ATTANSIC_L1C:
+ hw->nic_type = athr_l1c;
+ break;
+ case PCI_DEVICE_ID_ATHEROS_L2C_B:
+ hw->nic_type = athr_l2c_b;
+ break;
+ case PCI_DEVICE_ID_ATHEROS_L2C_B2:
+ hw->nic_type = athr_l2c_b2;
+ break;
+ case PCI_DEVICE_ID_ATHEROS_L1D:
+ hw->nic_type = athr_l1d;
+ break;
+ case PCI_DEVICE_ID_ATHEROS_L1D_2_0:
+ hw->nic_type = athr_l1d_2;
+ break;
+ default:
+ break;
+ }
+}
+
+static int atl1c_setup_mac_funcs(struct atl1c_hw *hw)
+{
+ u32 phy_status_data;
+ u32 link_ctrl_data;
+
+ atl1c_set_mac_type(hw);
+ AT_READ_REG(hw, REG_PHY_STATUS, &phy_status_data);
+ AT_READ_REG(hw, REG_LINK_CTRL, &link_ctrl_data);
+
+ hw->ctrl_flags = ATL1C_INTR_MODRT_ENABLE |
+ ATL1C_TXQ_MODE_ENHANCE;
+ if (link_ctrl_data & LINK_CTRL_L0S_EN)
+ hw->ctrl_flags |= ATL1C_ASPM_L0S_SUPPORT;
+ if (link_ctrl_data & LINK_CTRL_L1_EN)
+ hw->ctrl_flags |= ATL1C_ASPM_L1_SUPPORT;
+ if (link_ctrl_data & LINK_CTRL_EXT_SYNC)
+ hw->ctrl_flags |= ATL1C_LINK_EXT_SYNC;
+ hw->ctrl_flags |= ATL1C_ASPM_CTRL_MON;
+
+ if (hw->nic_type == athr_l1c ||
+ hw->nic_type == athr_l1d ||
+ hw->nic_type == athr_l1d_2)
+ hw->link_cap_flags |= ATL1C_LINK_CAP_1000M;
+ return 0;
+}
+/*
+ * atl1c_sw_init - Initialize general software structures (struct atl1c_adapter)
+ * @adapter: board private structure to initialize
+ *
+ * atl1c_sw_init initializes the Adapter private data structure.
+ * Fields are initialized based on PCI device information and
+ * OS network device settings (MTU size).
+ */
+static int __devinit atl1c_sw_init(struct atl1c_adapter *adapter)
+{
+ struct atl1c_hw *hw = &adapter->hw;
+ struct pci_dev *pdev = adapter->pdev;
+ u32 revision;
+
+
+ adapter->wol = 0;
+ device_set_wakeup_enable(&pdev->dev, false);
+ adapter->link_speed = SPEED_0;
+ adapter->link_duplex = FULL_DUPLEX;
+ adapter->num_rx_queues = AT_DEF_RECEIVE_QUEUE;
+ adapter->tpd_ring[0].count = 1024;
+ adapter->rfd_ring[0].count = 512;
+
+ hw->vendor_id = pdev->vendor;
+ hw->device_id = pdev->device;
+ hw->subsystem_vendor_id = pdev->subsystem_vendor;
+ hw->subsystem_id = pdev->subsystem_device;
+ AT_READ_REG(hw, PCI_CLASS_REVISION, &revision);
+ hw->revision_id = revision & 0xFF;
+ /* before link up, we assume hibernate is true */
+ hw->hibernate = true;
+ hw->media_type = MEDIA_TYPE_AUTO_SENSOR;
+ if (atl1c_setup_mac_funcs(hw) != 0) {
+ dev_err(&pdev->dev, "set mac function pointers failed\n");
+ return -1;
+ }
+ hw->intr_mask = IMR_NORMAL_MASK;
+ hw->phy_configured = false;
+ hw->preamble_len = 7;
+ hw->max_frame_size = adapter->netdev->mtu;
+ if (adapter->num_rx_queues < 2) {
+ hw->rss_type = atl1c_rss_disable;
+ hw->rss_mode = atl1c_rss_mode_disable;
+ } else {
+ hw->rss_type = atl1c_rss_ipv4;
+ hw->rss_mode = atl1c_rss_mul_que_mul_int;
+ hw->rss_hash_bits = 16;
+ }
+ hw->autoneg_advertised = ADVERTISED_Autoneg;
+ hw->indirect_tab = 0xE4E4E4E4;
+ hw->base_cpu = 0;
+
+ hw->ict = 50000; /* 100ms */
+ hw->smb_timer = 200000; /* 400ms */
+ hw->cmb_tpd = 4;
+ hw->cmb_tx_timer = 1; /* 2 us */
+ hw->rx_imt = 200;
+ hw->tx_imt = 1000;
+
+ hw->tpd_burst = 5;
+ hw->rfd_burst = 8;
+ hw->dma_order = atl1c_dma_ord_out;
+ hw->dmar_block = atl1c_dma_req_1024;
+ hw->dmaw_block = atl1c_dma_req_1024;
+ hw->dmar_dly_cnt = 15;
+ hw->dmaw_dly_cnt = 4;
+
+ if (atl1c_alloc_queues(adapter)) {
+ dev_err(&pdev->dev, "Unable to allocate memory for queues\n");
+ return -ENOMEM;
+ }
+ /* TODO */
+ atl1c_set_rxbufsize(adapter, adapter->netdev);
+ atomic_set(&adapter->irq_sem, 1);
+ spin_lock_init(&adapter->mdio_lock);
+ spin_lock_init(&adapter->tx_lock);
+ set_bit(__AT_DOWN, &adapter->flags);
+
+ return 0;
+}
+
+static inline void atl1c_clean_buffer(struct pci_dev *pdev,
+ struct atl1c_buffer *buffer_info, int in_irq)
+{
+ u16 pci_driection;
+ if (buffer_info->flags & ATL1C_BUFFER_FREE)
+ return;
+ if (buffer_info->dma) {
+ if (buffer_info->flags & ATL1C_PCIMAP_FROMDEVICE)
+ pci_driection = PCI_DMA_FROMDEVICE;
+ else
+ pci_driection = PCI_DMA_TODEVICE;
+
+ if (buffer_info->flags & ATL1C_PCIMAP_SINGLE)
+ pci_unmap_single(pdev, buffer_info->dma,
+ buffer_info->length, pci_driection);
+ else if (buffer_info->flags & ATL1C_PCIMAP_PAGE)
+ pci_unmap_page(pdev, buffer_info->dma,
+ buffer_info->length, pci_driection);
+ }
+ if (buffer_info->skb) {
+ if (in_irq)
+ dev_kfree_skb_irq(buffer_info->skb);
+ else
+ dev_kfree_skb(buffer_info->skb);
+ }
+ buffer_info->dma = 0;
+ buffer_info->skb = NULL;
+ ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_FREE);
+}
+/*
+ * atl1c_clean_tx_ring - Free Tx-skb
+ * @adapter: board private structure
+ */
+static void atl1c_clean_tx_ring(struct atl1c_adapter *adapter,
+ enum atl1c_trans_queue type)
+{
+ struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type];
+ struct atl1c_buffer *buffer_info;
+ struct pci_dev *pdev = adapter->pdev;
+ u16 index, ring_count;
+
+ ring_count = tpd_ring->count;
+ for (index = 0; index < ring_count; index++) {
+ buffer_info = &tpd_ring->buffer_info[index];
+ atl1c_clean_buffer(pdev, buffer_info, 0);
+ }
+
+ /* Zero out Tx-buffers */
+ memset(tpd_ring->desc, 0, sizeof(struct atl1c_tpd_desc) *
+ ring_count);
+ atomic_set(&tpd_ring->next_to_clean, 0);
+ tpd_ring->next_to_use = 0;
+}
+
+/*
+ * atl1c_clean_rx_ring - Free rx-reservation skbs
+ * @adapter: board private structure
+ */
+static void atl1c_clean_rx_ring(struct atl1c_adapter *adapter)
+{
+ struct atl1c_rfd_ring *rfd_ring = adapter->rfd_ring;
+ struct atl1c_rrd_ring *rrd_ring = adapter->rrd_ring;
+ struct atl1c_buffer *buffer_info;
+ struct pci_dev *pdev = adapter->pdev;
+ int i, j;
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ for (j = 0; j < rfd_ring[i].count; j++) {
+ buffer_info = &rfd_ring[i].buffer_info[j];
+ atl1c_clean_buffer(pdev, buffer_info, 0);
+ }
+ /* zero out the descriptor ring */
+ memset(rfd_ring[i].desc, 0, rfd_ring[i].size);
+ rfd_ring[i].next_to_clean = 0;
+ rfd_ring[i].next_to_use = 0;
+ rrd_ring[i].next_to_use = 0;
+ rrd_ring[i].next_to_clean = 0;
+ }
+}
+
+/*
+ * Read / Write Ptr Initialize:
+ */
+static void atl1c_init_ring_ptrs(struct atl1c_adapter *adapter)
+{
+ struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring;
+ struct atl1c_rfd_ring *rfd_ring = adapter->rfd_ring;
+ struct atl1c_rrd_ring *rrd_ring = adapter->rrd_ring;
+ struct atl1c_buffer *buffer_info;
+ int i, j;
+
+ for (i = 0; i < AT_MAX_TRANSMIT_QUEUE; i++) {
+ tpd_ring[i].next_to_use = 0;
+ atomic_set(&tpd_ring[i].next_to_clean, 0);
+ buffer_info = tpd_ring[i].buffer_info;
+ for (j = 0; j < tpd_ring->count; j++)
+ ATL1C_SET_BUFFER_STATE(&buffer_info[i],
+ ATL1C_BUFFER_FREE);
+ }
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ rfd_ring[i].next_to_use = 0;
+ rfd_ring[i].next_to_clean = 0;
+ rrd_ring[i].next_to_use = 0;
+ rrd_ring[i].next_to_clean = 0;
+ for (j = 0; j < rfd_ring[i].count; j++) {
+ buffer_info = &rfd_ring[i].buffer_info[j];
+ ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_FREE);
+ }
+ }
+}
+
+/*
+ * atl1c_free_ring_resources - Free Tx / RX descriptor Resources
+ * @adapter: board private structure
+ *
+ * Free all transmit software resources
+ */
+static void atl1c_free_ring_resources(struct atl1c_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+
+ pci_free_consistent(pdev, adapter->ring_header.size,
+ adapter->ring_header.desc,
+ adapter->ring_header.dma);
+ adapter->ring_header.desc = NULL;
+
+ /* Note: just free tdp_ring.buffer_info,
+ * it contain rfd_ring.buffer_info, do not double free */
+ if (adapter->tpd_ring[0].buffer_info) {
+ kfree(adapter->tpd_ring[0].buffer_info);
+ adapter->tpd_ring[0].buffer_info = NULL;
+ }
+}
+
+/*
+ * atl1c_setup_mem_resources - allocate Tx / RX descriptor resources
+ * @adapter: board private structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static int atl1c_setup_ring_resources(struct atl1c_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring;
+ struct atl1c_rfd_ring *rfd_ring = adapter->rfd_ring;
+ struct atl1c_rrd_ring *rrd_ring = adapter->rrd_ring;
+ struct atl1c_ring_header *ring_header = &adapter->ring_header;
+ int num_rx_queues = adapter->num_rx_queues;
+ int size;
+ int i;
+ int count = 0;
+ int rx_desc_count = 0;
+ u32 offset = 0;
+
+ rrd_ring[0].count = rfd_ring[0].count;
+ for (i = 1; i < AT_MAX_TRANSMIT_QUEUE; i++)
+ tpd_ring[i].count = tpd_ring[0].count;
+
+ for (i = 1; i < adapter->num_rx_queues; i++)
+ rfd_ring[i].count = rrd_ring[i].count = rfd_ring[0].count;
+
+ /* 2 tpd queue, one high priority queue,
+ * another normal priority queue */
+ size = sizeof(struct atl1c_buffer) * (tpd_ring->count * 2 +
+ rfd_ring->count * num_rx_queues);
+ tpd_ring->buffer_info = kzalloc(size, GFP_KERNEL);
+ if (unlikely(!tpd_ring->buffer_info)) {
+ dev_err(&pdev->dev, "kzalloc failed, size = %d\n",
+ size);
+ goto err_nomem;
+ }
+ for (i = 0; i < AT_MAX_TRANSMIT_QUEUE; i++) {
+ tpd_ring[i].buffer_info =
+ (struct atl1c_buffer *) (tpd_ring->buffer_info + count);
+ count += tpd_ring[i].count;
+ }
+
+ for (i = 0; i < num_rx_queues; i++) {
+ rfd_ring[i].buffer_info =
+ (struct atl1c_buffer *) (tpd_ring->buffer_info + count);
+ count += rfd_ring[i].count;
+ rx_desc_count += rfd_ring[i].count;
+ }
+ /*
+ * real ring DMA buffer
+ * each ring/block may need up to 8 bytes for alignment, hence the
+ * additional bytes tacked onto the end.
+ */
+ ring_header->size = size =
+ sizeof(struct atl1c_tpd_desc) * tpd_ring->count * 2 +
+ sizeof(struct atl1c_rx_free_desc) * rx_desc_count +
+ sizeof(struct atl1c_recv_ret_status) * rx_desc_count +
+ sizeof(struct atl1c_hw_stats) +
+ 8 * 4 + 8 * 2 * num_rx_queues;
+
+ ring_header->desc = pci_alloc_consistent(pdev, ring_header->size,
+ &ring_header->dma);
+ if (unlikely(!ring_header->desc)) {
+ dev_err(&pdev->dev, "pci_alloc_consistend failed\n");
+ goto err_nomem;
+ }
+ memset(ring_header->desc, 0, ring_header->size);
+ /* init TPD ring */
+
+ tpd_ring[0].dma = roundup(ring_header->dma, 8);
+ offset = tpd_ring[0].dma - ring_header->dma;
+ for (i = 0; i < AT_MAX_TRANSMIT_QUEUE; i++) {
+ tpd_ring[i].dma = ring_header->dma + offset;
+ tpd_ring[i].desc = (u8 *) ring_header->desc + offset;
+ tpd_ring[i].size =
+ sizeof(struct atl1c_tpd_desc) * tpd_ring[i].count;
+ offset += roundup(tpd_ring[i].size, 8);
+ }
+ /* init RFD ring */
+ for (i = 0; i < num_rx_queues; i++) {
+ rfd_ring[i].dma = ring_header->dma + offset;
+ rfd_ring[i].desc = (u8 *) ring_header->desc + offset;
+ rfd_ring[i].size = sizeof(struct atl1c_rx_free_desc) *
+ rfd_ring[i].count;
+ offset += roundup(rfd_ring[i].size, 8);
+ }
+
+ /* init RRD ring */
+ for (i = 0; i < num_rx_queues; i++) {
+ rrd_ring[i].dma = ring_header->dma + offset;
+ rrd_ring[i].desc = (u8 *) ring_header->desc + offset;
+ rrd_ring[i].size = sizeof(struct atl1c_recv_ret_status) *
+ rrd_ring[i].count;
+ offset += roundup(rrd_ring[i].size, 8);
+ }
+
+ adapter->smb.dma = ring_header->dma + offset;
+ adapter->smb.smb = (u8 *)ring_header->desc + offset;
+ return 0;
+
+err_nomem:
+ kfree(tpd_ring->buffer_info);
+ return -ENOMEM;
+}
+
+static void atl1c_configure_des_ring(struct atl1c_adapter *adapter)
+{
+ struct atl1c_hw *hw = &adapter->hw;
+ struct atl1c_rfd_ring *rfd_ring = (struct atl1c_rfd_ring *)
+ adapter->rfd_ring;
+ struct atl1c_rrd_ring *rrd_ring = (struct atl1c_rrd_ring *)
+ adapter->rrd_ring;
+ struct atl1c_tpd_ring *tpd_ring = (struct atl1c_tpd_ring *)
+ adapter->tpd_ring;
+ struct atl1c_cmb *cmb = (struct atl1c_cmb *) &adapter->cmb;
+ struct atl1c_smb *smb = (struct atl1c_smb *) &adapter->smb;
+ int i;
+ u32 data;
+
+ /* TPD */
+ AT_WRITE_REG(hw, REG_TX_BASE_ADDR_HI,
+ (u32)((tpd_ring[atl1c_trans_normal].dma &
+ AT_DMA_HI_ADDR_MASK) >> 32));
+ /* just enable normal priority TX queue */
+ AT_WRITE_REG(hw, REG_NTPD_HEAD_ADDR_LO,
+ (u32)(tpd_ring[atl1c_trans_normal].dma &
+ AT_DMA_LO_ADDR_MASK));
+ AT_WRITE_REG(hw, REG_HTPD_HEAD_ADDR_LO,
+ (u32)(tpd_ring[atl1c_trans_high].dma &
+ AT_DMA_LO_ADDR_MASK));
+ AT_WRITE_REG(hw, REG_TPD_RING_SIZE,
+ (u32)(tpd_ring[0].count & TPD_RING_SIZE_MASK));
+
+
+ /* RFD */
+ AT_WRITE_REG(hw, REG_RX_BASE_ADDR_HI,
+ (u32)((rfd_ring[0].dma & AT_DMA_HI_ADDR_MASK) >> 32));
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ AT_WRITE_REG(hw, atl1c_rfd_addr_lo_regs[i],
+ (u32)(rfd_ring[i].dma & AT_DMA_LO_ADDR_MASK));
+
+ AT_WRITE_REG(hw, REG_RFD_RING_SIZE,
+ rfd_ring[0].count & RFD_RING_SIZE_MASK);
+ AT_WRITE_REG(hw, REG_RX_BUF_SIZE,
+ adapter->rx_buffer_len & RX_BUF_SIZE_MASK);
+
+ /* RRD */
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ AT_WRITE_REG(hw, atl1c_rrd_addr_lo_regs[i],
+ (u32)(rrd_ring[i].dma & AT_DMA_LO_ADDR_MASK));
+ AT_WRITE_REG(hw, REG_RRD_RING_SIZE,
+ (rrd_ring[0].count & RRD_RING_SIZE_MASK));
+
+ /* CMB */
+ AT_WRITE_REG(hw, REG_CMB_BASE_ADDR_LO, cmb->dma & AT_DMA_LO_ADDR_MASK);
+
+ /* SMB */
+ AT_WRITE_REG(hw, REG_SMB_BASE_ADDR_HI,
+ (u32)((smb->dma & AT_DMA_HI_ADDR_MASK) >> 32));
+ AT_WRITE_REG(hw, REG_SMB_BASE_ADDR_LO,
+ (u32)(smb->dma & AT_DMA_LO_ADDR_MASK));
+ if (hw->nic_type == athr_l2c_b) {
+ AT_WRITE_REG(hw, REG_SRAM_RXF_LEN, 0x02a0L);
+ AT_WRITE_REG(hw, REG_SRAM_TXF_LEN, 0x0100L);
+ AT_WRITE_REG(hw, REG_SRAM_RXF_ADDR, 0x029f0000L);
+ AT_WRITE_REG(hw, REG_SRAM_RFD0_INFO, 0x02bf02a0L);
+ AT_WRITE_REG(hw, REG_SRAM_TXF_ADDR, 0x03bf02c0L);
+ AT_WRITE_REG(hw, REG_SRAM_TRD_ADDR, 0x03df03c0L);
+ AT_WRITE_REG(hw, REG_TXF_WATER_MARK, 0); /* TX watermark, to enter l1 state.*/
+ AT_WRITE_REG(hw, REG_RXD_DMA_CTRL, 0); /* RXD threshold.*/
+ }
+ if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d_2) {
+ /* Power Saving for L2c_B */
+ AT_READ_REG(hw, REG_SERDES_LOCK, &data);
+ data |= SERDES_MAC_CLK_SLOWDOWN;
+ data |= SERDES_PYH_CLK_SLOWDOWN;
+ AT_WRITE_REG(hw, REG_SERDES_LOCK, data);
+ }
+ /* Load all of base address above */
+ AT_WRITE_REG(hw, REG_LOAD_PTR, 1);
+}
+
+static void atl1c_configure_tx(struct atl1c_adapter *adapter)
+{
+ struct atl1c_hw *hw = &adapter->hw;
+ u32 dev_ctrl_data;
+ u32 max_pay_load;
+ u16 tx_offload_thresh;
+ u32 txq_ctrl_data;
+ u32 max_pay_load_data;
+
+ tx_offload_thresh = MAX_TX_OFFLOAD_THRESH;
+ AT_WRITE_REG(hw, REG_TX_TSO_OFFLOAD_THRESH,
+ (tx_offload_thresh >> 3) & TX_TSO_OFFLOAD_THRESH_MASK);
+ AT_READ_REG(hw, REG_DEVICE_CTRL, &dev_ctrl_data);
+ max_pay_load = (dev_ctrl_data >> DEVICE_CTRL_MAX_PAYLOAD_SHIFT) &
+ DEVICE_CTRL_MAX_PAYLOAD_MASK;
+ hw->dmaw_block = min_t(u32, max_pay_load, hw->dmaw_block);
+ max_pay_load = (dev_ctrl_data >> DEVICE_CTRL_MAX_RREQ_SZ_SHIFT) &
+ DEVICE_CTRL_MAX_RREQ_SZ_MASK;
+ hw->dmar_block = min_t(u32, max_pay_load, hw->dmar_block);
+
+ txq_ctrl_data = (hw->tpd_burst & TXQ_NUM_TPD_BURST_MASK) <<
+ TXQ_NUM_TPD_BURST_SHIFT;
+ if (hw->ctrl_flags & ATL1C_TXQ_MODE_ENHANCE)
+ txq_ctrl_data |= TXQ_CTRL_ENH_MODE;
+ max_pay_load_data = (atl1c_pay_load_size[hw->dmar_block] &
+ TXQ_TXF_BURST_NUM_MASK) << TXQ_TXF_BURST_NUM_SHIFT;
+ if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2)
+ max_pay_load_data >>= 1;
+ txq_ctrl_data |= max_pay_load_data;
+
+ AT_WRITE_REG(hw, REG_TXQ_CTRL, txq_ctrl_data);
+}
+
+static void atl1c_configure_rx(struct atl1c_adapter *adapter)
+{
+ struct atl1c_hw *hw = &adapter->hw;
+ u32 rxq_ctrl_data;
+
+ rxq_ctrl_data = (hw->rfd_burst & RXQ_RFD_BURST_NUM_MASK) <<
+ RXQ_RFD_BURST_NUM_SHIFT;
+
+ if (hw->ctrl_flags & ATL1C_RX_IPV6_CHKSUM)
+ rxq_ctrl_data |= IPV6_CHKSUM_CTRL_EN;
+ if (hw->rss_type == atl1c_rss_ipv4)
+ rxq_ctrl_data |= RSS_HASH_IPV4;
+ if (hw->rss_type == atl1c_rss_ipv4_tcp)
+ rxq_ctrl_data |= RSS_HASH_IPV4_TCP;
+ if (hw->rss_type == atl1c_rss_ipv6)
+ rxq_ctrl_data |= RSS_HASH_IPV6;
+ if (hw->rss_type == atl1c_rss_ipv6_tcp)
+ rxq_ctrl_data |= RSS_HASH_IPV6_TCP;
+ if (hw->rss_type != atl1c_rss_disable)
+ rxq_ctrl_data |= RRS_HASH_CTRL_EN;
+
+ rxq_ctrl_data |= (hw->rss_mode & RSS_MODE_MASK) <<
+ RSS_MODE_SHIFT;
+ rxq_ctrl_data |= (hw->rss_hash_bits & RSS_HASH_BITS_MASK) <<
+ RSS_HASH_BITS_SHIFT;
+ if (hw->ctrl_flags & ATL1C_ASPM_CTRL_MON)
+ rxq_ctrl_data |= (ASPM_THRUPUT_LIMIT_1M &
+ ASPM_THRUPUT_LIMIT_MASK) << ASPM_THRUPUT_LIMIT_SHIFT;
+
+ AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq_ctrl_data);
+}
+
+static void atl1c_configure_rss(struct atl1c_adapter *adapter)
+{
+ struct atl1c_hw *hw = &adapter->hw;
+
+ AT_WRITE_REG(hw, REG_IDT_TABLE, hw->indirect_tab);
+ AT_WRITE_REG(hw, REG_BASE_CPU_NUMBER, hw->base_cpu);
+}
+
+static void atl1c_configure_dma(struct atl1c_adapter *adapter)
+{
+ struct atl1c_hw *hw = &adapter->hw;
+ u32 dma_ctrl_data;
+
+ dma_ctrl_data = DMA_CTRL_DMAR_REQ_PRI;
+ if (hw->ctrl_flags & ATL1C_CMB_ENABLE)
+ dma_ctrl_data |= DMA_CTRL_CMB_EN;
+ if (hw->ctrl_flags & ATL1C_SMB_ENABLE)
+ dma_ctrl_data |= DMA_CTRL_SMB_EN;
+ else
+ dma_ctrl_data |= MAC_CTRL_SMB_DIS;
+
+ switch (hw->dma_order) {
+ case atl1c_dma_ord_in:
+ dma_ctrl_data |= DMA_CTRL_DMAR_IN_ORDER;
+ break;
+ case atl1c_dma_ord_enh:
+ dma_ctrl_data |= DMA_CTRL_DMAR_ENH_ORDER;
+ break;
+ case atl1c_dma_ord_out:
+ dma_ctrl_data |= DMA_CTRL_DMAR_OUT_ORDER;
+ break;
+ default:
+ break;
+ }
+
+ dma_ctrl_data |= (((u32)hw->dmar_block) & DMA_CTRL_DMAR_BURST_LEN_MASK)
+ << DMA_CTRL_DMAR_BURST_LEN_SHIFT;
+ dma_ctrl_data |= (((u32)hw->dmaw_block) & DMA_CTRL_DMAW_BURST_LEN_MASK)
+ << DMA_CTRL_DMAW_BURST_LEN_SHIFT;
+ dma_ctrl_data |= (((u32)hw->dmar_dly_cnt) & DMA_CTRL_DMAR_DLY_CNT_MASK)
+ << DMA_CTRL_DMAR_DLY_CNT_SHIFT;
+ dma_ctrl_data |= (((u32)hw->dmaw_dly_cnt) & DMA_CTRL_DMAW_DLY_CNT_MASK)
+ << DMA_CTRL_DMAW_DLY_CNT_SHIFT;
+
+ AT_WRITE_REG(hw, REG_DMA_CTRL, dma_ctrl_data);
+}
+
+/*
+ * Stop the mac, transmit and receive units
+ * hw - Struct containing variables accessed by shared code
+ * return : 0 or idle status (if error)
+ */
+static int atl1c_stop_mac(struct atl1c_hw *hw)
+{
+ u32 data;
+
+ AT_READ_REG(hw, REG_RXQ_CTRL, &data);
+ data &= ~(RXQ1_CTRL_EN | RXQ2_CTRL_EN |
+ RXQ3_CTRL_EN | RXQ_CTRL_EN);
+ AT_WRITE_REG(hw, REG_RXQ_CTRL, data);
+
+ AT_READ_REG(hw, REG_TXQ_CTRL, &data);
+ data &= ~TXQ_CTRL_EN;
+ AT_WRITE_REG(hw, REG_TWSI_CTRL, data);
+
+ atl1c_wait_until_idle(hw);
+
+ AT_READ_REG(hw, REG_MAC_CTRL, &data);
+ data &= ~(MAC_CTRL_TX_EN | MAC_CTRL_RX_EN);
+ AT_WRITE_REG(hw, REG_MAC_CTRL, data);
+
+ return (int)atl1c_wait_until_idle(hw);
+}
+
+static void atl1c_enable_rx_ctrl(struct atl1c_hw *hw)
+{
+ u32 data;
+
+ AT_READ_REG(hw, REG_RXQ_CTRL, &data);
+ switch (hw->adapter->num_rx_queues) {
+ case 4:
+ data |= (RXQ3_CTRL_EN | RXQ2_CTRL_EN | RXQ1_CTRL_EN);
+ break;
+ case 3:
+ data |= (RXQ2_CTRL_EN | RXQ1_CTRL_EN);
+ break;
+ case 2:
+ data |= RXQ1_CTRL_EN;
+ break;
+ default:
+ break;
+ }
+ data |= RXQ_CTRL_EN;
+ AT_WRITE_REG(hw, REG_RXQ_CTRL, data);
+}
+
+static void atl1c_enable_tx_ctrl(struct atl1c_hw *hw)
+{
+ u32 data;
+
+ AT_READ_REG(hw, REG_TXQ_CTRL, &data);
+ data |= TXQ_CTRL_EN;
+ AT_WRITE_REG(hw, REG_TXQ_CTRL, data);
+}
+
+/*
+ * Reset the transmit and receive units; mask and clear all interrupts.
+ * hw - Struct containing variables accessed by shared code
+ * return : 0 or idle status (if error)
+ */
+static int atl1c_reset_mac(struct atl1c_hw *hw)
+{
+ struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter;
+ struct pci_dev *pdev = adapter->pdev;
+ u32 master_ctrl_data = 0;
+
+ AT_WRITE_REG(hw, REG_IMR, 0);
+ AT_WRITE_REG(hw, REG_ISR, ISR_DIS_INT);
+
+ atl1c_stop_mac(hw);
+ /*
+ * Issue Soft Reset to the MAC. This will reset the chip's
+ * transmit, receive, DMA. It will not effect
+ * the current PCI configuration. The global reset bit is self-
+ * clearing, and should clear within a microsecond.
+ */
+ AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl_data);
+ master_ctrl_data |= MASTER_CTRL_OOB_DIS_OFF;
+ AT_WRITE_REGW(hw, REG_MASTER_CTRL, ((master_ctrl_data | MASTER_CTRL_SOFT_RST)
+ & 0xFFFF));
+
+ AT_WRITE_FLUSH(hw);
+ msleep(10);
+ /* Wait at least 10ms for All module to be Idle */
+
+ if (atl1c_wait_until_idle(hw)) {
+ dev_err(&pdev->dev,
+ "MAC state machine can't be idle since"
+ " disabled for 10ms second\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void atl1c_disable_l0s_l1(struct atl1c_hw *hw)
+{
+ u32 pm_ctrl_data;
+
+ AT_READ_REG(hw, REG_PM_CTRL, &pm_ctrl_data);
+ pm_ctrl_data &= ~(PM_CTRL_L1_ENTRY_TIMER_MASK <<
+ PM_CTRL_L1_ENTRY_TIMER_SHIFT);
+ pm_ctrl_data &= ~PM_CTRL_CLK_SWH_L1;
+ pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
+ pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN;
+ pm_ctrl_data &= ~PM_CTRL_MAC_ASPM_CHK;
+ pm_ctrl_data &= ~PM_CTRL_SERDES_PD_EX_L1;
+
+ pm_ctrl_data |= PM_CTRL_SERDES_BUDS_RX_L1_EN;
+ pm_ctrl_data |= PM_CTRL_SERDES_PLL_L1_EN;
+ pm_ctrl_data |= PM_CTRL_SERDES_L1_EN;
+ AT_WRITE_REG(hw, REG_PM_CTRL, pm_ctrl_data);
+}
+
+/*
+ * Set ASPM state.
+ * Enable/disable L0s/L1 depend on link state.
+ */
+static void atl1c_set_aspm(struct atl1c_hw *hw, bool linkup)
+{
+ u32 pm_ctrl_data;
+ u32 link_ctrl_data;
+ u32 link_l1_timer = 0xF;
+
+ AT_READ_REG(hw, REG_PM_CTRL, &pm_ctrl_data);
+ AT_READ_REG(hw, REG_LINK_CTRL, &link_ctrl_data);
+
+ pm_ctrl_data &= ~PM_CTRL_SERDES_PD_EX_L1;
+ pm_ctrl_data &= ~(PM_CTRL_L1_ENTRY_TIMER_MASK <<
+ PM_CTRL_L1_ENTRY_TIMER_SHIFT);
+ pm_ctrl_data &= ~(PM_CTRL_LCKDET_TIMER_MASK <<
+ PM_CTRL_LCKDET_TIMER_SHIFT);
+ pm_ctrl_data |= AT_LCKDET_TIMER << PM_CTRL_LCKDET_TIMER_SHIFT;
+
+ if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d ||
+ hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) {
+ link_ctrl_data &= ~LINK_CTRL_EXT_SYNC;
+ if (!(hw->ctrl_flags & ATL1C_APS_MODE_ENABLE)) {
+ if (hw->nic_type == athr_l2c_b && hw->revision_id == L2CB_V10)
+ link_ctrl_data |= LINK_CTRL_EXT_SYNC;
+ }
+
+ AT_WRITE_REG(hw, REG_LINK_CTRL, link_ctrl_data);
+
+ pm_ctrl_data |= PM_CTRL_RCVR_WT_TIMER;
+ pm_ctrl_data &= ~(PM_CTRL_PM_REQ_TIMER_MASK <<
+ PM_CTRL_PM_REQ_TIMER_SHIFT);
+ pm_ctrl_data |= AT_ASPM_L1_TIMER <<
+ PM_CTRL_PM_REQ_TIMER_SHIFT;
+ pm_ctrl_data &= ~PM_CTRL_SA_DLY_EN;
+ pm_ctrl_data &= ~PM_CTRL_HOTRST;
+ pm_ctrl_data |= 1 << PM_CTRL_L1_ENTRY_TIMER_SHIFT;
+ pm_ctrl_data |= PM_CTRL_SERDES_PD_EX_L1;
+ }
+ pm_ctrl_data |= PM_CTRL_MAC_ASPM_CHK;
+ if (linkup) {
+ pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN;
+ pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
+ if (hw->ctrl_flags & ATL1C_ASPM_L1_SUPPORT)
+ pm_ctrl_data |= PM_CTRL_ASPM_L1_EN;
+ if (hw->ctrl_flags & ATL1C_ASPM_L0S_SUPPORT)
+ pm_ctrl_data |= PM_CTRL_ASPM_L0S_EN;
+
+ if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d ||
+ hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) {
+ if (hw->nic_type == athr_l2c_b)
+ if (!(hw->ctrl_flags & ATL1C_APS_MODE_ENABLE))
+ pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
+ pm_ctrl_data &= ~PM_CTRL_SERDES_L1_EN;
+ pm_ctrl_data &= ~PM_CTRL_SERDES_PLL_L1_EN;
+ pm_ctrl_data &= ~PM_CTRL_SERDES_BUDS_RX_L1_EN;
+ pm_ctrl_data |= PM_CTRL_CLK_SWH_L1;
+ if (hw->adapter->link_speed == SPEED_100 ||
+ hw->adapter->link_speed == SPEED_1000) {
+ pm_ctrl_data &= ~(PM_CTRL_L1_ENTRY_TIMER_MASK <<
+ PM_CTRL_L1_ENTRY_TIMER_SHIFT);
+ if (hw->nic_type == athr_l2c_b)
+ link_l1_timer = 7;
+ else if (hw->nic_type == athr_l2c_b2 ||
+ hw->nic_type == athr_l1d_2)
+ link_l1_timer = 4;
+ pm_ctrl_data |= link_l1_timer <<
+ PM_CTRL_L1_ENTRY_TIMER_SHIFT;
+ }
+ } else {
+ pm_ctrl_data |= PM_CTRL_SERDES_L1_EN;
+ pm_ctrl_data |= PM_CTRL_SERDES_PLL_L1_EN;
+ pm_ctrl_data |= PM_CTRL_SERDES_BUDS_RX_L1_EN;
+ pm_ctrl_data &= ~PM_CTRL_CLK_SWH_L1;
+ pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
+ pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN;
+
+ }
+ } else {
+ pm_ctrl_data &= ~PM_CTRL_SERDES_L1_EN;
+ pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
+ pm_ctrl_data &= ~PM_CTRL_SERDES_PLL_L1_EN;
+ pm_ctrl_data |= PM_CTRL_CLK_SWH_L1;
+
+ if (hw->ctrl_flags & ATL1C_ASPM_L1_SUPPORT)
+ pm_ctrl_data |= PM_CTRL_ASPM_L1_EN;
+ else
+ pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN;
+ }
+ AT_WRITE_REG(hw, REG_PM_CTRL, pm_ctrl_data);
+
+ return;
+}
+
+static void atl1c_setup_mac_ctrl(struct atl1c_adapter *adapter)
+{
+ struct atl1c_hw *hw = &adapter->hw;
+ struct net_device *netdev = adapter->netdev;
+ u32 mac_ctrl_data;
+
+ mac_ctrl_data = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN;
+ mac_ctrl_data |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW);
+
+ if (adapter->link_duplex == FULL_DUPLEX) {
+ hw->mac_duplex = true;
+ mac_ctrl_data |= MAC_CTRL_DUPLX;
+ }
+
+ if (adapter->link_speed == SPEED_1000)
+ hw->mac_speed = atl1c_mac_speed_1000;
+ else
+ hw->mac_speed = atl1c_mac_speed_10_100;
+
+ mac_ctrl_data |= (hw->mac_speed & MAC_CTRL_SPEED_MASK) <<
+ MAC_CTRL_SPEED_SHIFT;
+
+ mac_ctrl_data |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD);
+ mac_ctrl_data |= ((hw->preamble_len & MAC_CTRL_PRMLEN_MASK) <<
+ MAC_CTRL_PRMLEN_SHIFT);
+
+ __atl1c_vlan_mode(netdev->features, &mac_ctrl_data);
+
+ mac_ctrl_data |= MAC_CTRL_BC_EN;
+ if (netdev->flags & IFF_PROMISC)
+ mac_ctrl_data |= MAC_CTRL_PROMIS_EN;
+ if (netdev->flags & IFF_ALLMULTI)
+ mac_ctrl_data |= MAC_CTRL_MC_ALL_EN;
+
+ mac_ctrl_data |= MAC_CTRL_SINGLE_PAUSE_EN;
+ if (hw->nic_type == athr_l1d || hw->nic_type == athr_l2c_b2 ||
+ hw->nic_type == athr_l1d_2) {
+ mac_ctrl_data |= MAC_CTRL_SPEED_MODE_SW;
+ mac_ctrl_data |= MAC_CTRL_HASH_ALG_CRC32;
+ }
+ AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data);
+}
+
+/*
+ * atl1c_configure - Configure Transmit&Receive Unit after Reset
+ * @adapter: board private structure
+ *
+ * Configure the Tx /Rx unit of the MAC after a reset.
+ */
+static int atl1c_configure(struct atl1c_adapter *adapter)
+{
+ struct atl1c_hw *hw = &adapter->hw;
+ u32 master_ctrl_data = 0;
+ u32 intr_modrt_data;
+ u32 data;
+
+ /* clear interrupt status */
+ AT_WRITE_REG(hw, REG_ISR, 0xFFFFFFFF);
+ /* Clear any WOL status */
+ AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
+ /* set Interrupt Clear Timer
+ * HW will enable self to assert interrupt event to system after
+ * waiting x-time for software to notify it accept interrupt.
+ */
+
+ data = CLK_GATING_EN_ALL;
+ if (hw->ctrl_flags & ATL1C_CLK_GATING_EN) {
+ if (hw->nic_type == athr_l2c_b)
+ data &= ~CLK_GATING_RXMAC_EN;
+ } else
+ data = 0;
+ AT_WRITE_REG(hw, REG_CLK_GATING_CTRL, data);
+
+ AT_WRITE_REG(hw, REG_INT_RETRIG_TIMER,
+ hw->ict & INT_RETRIG_TIMER_MASK);
+
+ atl1c_configure_des_ring(adapter);
+
+ if (hw->ctrl_flags & ATL1C_INTR_MODRT_ENABLE) {
+ intr_modrt_data = (hw->tx_imt & IRQ_MODRT_TIMER_MASK) <<
+ IRQ_MODRT_TX_TIMER_SHIFT;
+ intr_modrt_data |= (hw->rx_imt & IRQ_MODRT_TIMER_MASK) <<
+ IRQ_MODRT_RX_TIMER_SHIFT;
+ AT_WRITE_REG(hw, REG_IRQ_MODRT_TIMER_INIT, intr_modrt_data);
+ master_ctrl_data |=
+ MASTER_CTRL_TX_ITIMER_EN | MASTER_CTRL_RX_ITIMER_EN;
+ }
+
+ if (hw->ctrl_flags & ATL1C_INTR_CLEAR_ON_READ)
+ master_ctrl_data |= MASTER_CTRL_INT_RDCLR;
+
+ master_ctrl_data |= MASTER_CTRL_SA_TIMER_EN;
+ AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data);
+
+ if (hw->ctrl_flags & ATL1C_CMB_ENABLE) {
+ AT_WRITE_REG(hw, REG_CMB_TPD_THRESH,
+ hw->cmb_tpd & CMB_TPD_THRESH_MASK);
+ AT_WRITE_REG(hw, REG_CMB_TX_TIMER,
+ hw->cmb_tx_timer & CMB_TX_TIMER_MASK);
+ }
+
+ if (hw->ctrl_flags & ATL1C_SMB_ENABLE)
+ AT_WRITE_REG(hw, REG_SMB_STAT_TIMER,
+ hw->smb_timer & SMB_STAT_TIMER_MASK);
+ /* set MTU */
+ AT_WRITE_REG(hw, REG_MTU, hw->max_frame_size + ETH_HLEN +
+ VLAN_HLEN + ETH_FCS_LEN);
+ /* HDS, disable */
+ AT_WRITE_REG(hw, REG_HDS_CTRL, 0);
+
+ atl1c_configure_tx(adapter);
+ atl1c_configure_rx(adapter);
+ atl1c_configure_rss(adapter);
+ atl1c_configure_dma(adapter);
+
+ return 0;
+}
+
+static void atl1c_update_hw_stats(struct atl1c_adapter *adapter)
+{
+ u16 hw_reg_addr = 0;
+ unsigned long *stats_item = NULL;
+ u32 data;
+
+ /* update rx status */
+ hw_reg_addr = REG_MAC_RX_STATUS_BIN;
+ stats_item = &adapter->hw_stats.rx_ok;
+ while (hw_reg_addr <= REG_MAC_RX_STATUS_END) {
+ AT_READ_REG(&adapter->hw, hw_reg_addr, &data);
+ *stats_item += data;
+ stats_item++;
+ hw_reg_addr += 4;
+ }
+/* update tx status */
+ hw_reg_addr = REG_MAC_TX_STATUS_BIN;
+ stats_item = &adapter->hw_stats.tx_ok;
+ while (hw_reg_addr <= REG_MAC_TX_STATUS_END) {
+ AT_READ_REG(&adapter->hw, hw_reg_addr, &data);
+ *stats_item += data;
+ stats_item++;
+ hw_reg_addr += 4;
+ }
+}
+
+/*
+ * atl1c_get_stats - Get System Network Statistics
+ * @netdev: network interface device structure
+ *
+ * Returns the address of the device statistics structure.
+ * The statistics are actually updated from the timer callback.
+ */
+static struct net_device_stats *atl1c_get_stats(struct net_device *netdev)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ struct atl1c_hw_stats *hw_stats = &adapter->hw_stats;
+ struct net_device_stats *net_stats = &netdev->stats;
+
+ atl1c_update_hw_stats(adapter);
+ net_stats->rx_packets = hw_stats->rx_ok;
+ net_stats->tx_packets = hw_stats->tx_ok;
+ net_stats->rx_bytes = hw_stats->rx_byte_cnt;
+ net_stats->tx_bytes = hw_stats->tx_byte_cnt;
+ net_stats->multicast = hw_stats->rx_mcast;
+ net_stats->collisions = hw_stats->tx_1_col +
+ hw_stats->tx_2_col * 2 +
+ hw_stats->tx_late_col + hw_stats->tx_abort_col;
+ net_stats->rx_errors = hw_stats->rx_frag + hw_stats->rx_fcs_err +
+ hw_stats->rx_len_err + hw_stats->rx_sz_ov +
+ hw_stats->rx_rrd_ov + hw_stats->rx_align_err;
+ net_stats->rx_fifo_errors = hw_stats->rx_rxf_ov;
+ net_stats->rx_length_errors = hw_stats->rx_len_err;
+ net_stats->rx_crc_errors = hw_stats->rx_fcs_err;
+ net_stats->rx_frame_errors = hw_stats->rx_align_err;
+ net_stats->rx_over_errors = hw_stats->rx_rrd_ov + hw_stats->rx_rxf_ov;
+
+ net_stats->rx_missed_errors = hw_stats->rx_rrd_ov + hw_stats->rx_rxf_ov;
+
+ net_stats->tx_errors = hw_stats->tx_late_col + hw_stats->tx_abort_col +
+ hw_stats->tx_underrun + hw_stats->tx_trunc;
+ net_stats->tx_fifo_errors = hw_stats->tx_underrun;
+ net_stats->tx_aborted_errors = hw_stats->tx_abort_col;
+ net_stats->tx_window_errors = hw_stats->tx_late_col;
+
+ return net_stats;
+}
+
+static inline void atl1c_clear_phy_int(struct atl1c_adapter *adapter)
+{
+ u16 phy_data;
+
+ spin_lock(&adapter->mdio_lock);
+ atl1c_read_phy_reg(&adapter->hw, MII_ISR, &phy_data);
+ spin_unlock(&adapter->mdio_lock);
+}
+
+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_buffer *buffer_info;
+ struct pci_dev *pdev = adapter->pdev;
+ u16 next_to_clean = atomic_read(&tpd_ring->next_to_clean);
+ u16 hw_next_to_clean;
+ u16 shift;
+ u32 data;
+
+ if (type == atl1c_trans_high)
+ shift = MB_HTPD_CONS_IDX_SHIFT;
+ else
+ shift = MB_NTPD_CONS_IDX_SHIFT;
+
+ AT_READ_REG(&adapter->hw, REG_MB_PRIO_CONS_IDX, &data);
+ hw_next_to_clean = (data >> shift) & MB_PRIO_PROD_IDX_MASK;
+
+ while (next_to_clean != hw_next_to_clean) {
+ buffer_info = &tpd_ring->buffer_info[next_to_clean];
+ atl1c_clean_buffer(pdev, buffer_info, 1);
+ if (++next_to_clean == tpd_ring->count)
+ next_to_clean = 0;
+ atomic_set(&tpd_ring->next_to_clean, next_to_clean);
+ }
+
+ if (netif_queue_stopped(adapter->netdev) &&
+ netif_carrier_ok(adapter->netdev)) {
+ netif_wake_queue(adapter->netdev);
+ }
+
+ return true;
+}
+
+/*
+ * atl1c_intr - Interrupt Handler
+ * @irq: interrupt number
+ * @data: pointer to a network interface device structure
+ * @pt_regs: CPU registers structure
+ */
+static irqreturn_t atl1c_intr(int irq, void *data)
+{
+ struct net_device *netdev = data;
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ struct pci_dev *pdev = adapter->pdev;
+ struct atl1c_hw *hw = &adapter->hw;
+ int max_ints = AT_MAX_INT_WORK;
+ int handled = IRQ_NONE;
+ u32 status;
+ u32 reg_data;
+
+ do {
+ AT_READ_REG(hw, REG_ISR, ®_data);
+ status = reg_data & hw->intr_mask;
+
+ if (status == 0 || (status & ISR_DIS_INT) != 0) {
+ if (max_ints != AT_MAX_INT_WORK)
+ handled = IRQ_HANDLED;
+ break;
+ }
+ /* link event */
+ if (status & ISR_GPHY)
+ atl1c_clear_phy_int(adapter);
+ /* Ack ISR */
+ AT_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT);
+ if (status & ISR_RX_PKT) {
+ if (likely(napi_schedule_prep(&adapter->napi))) {
+ hw->intr_mask &= ~ISR_RX_PKT;
+ AT_WRITE_REG(hw, REG_IMR, hw->intr_mask);
+ __napi_schedule(&adapter->napi);
+ }
+ }
+ if (status & ISR_TX_PKT)
+ atl1c_clean_tx_irq(adapter, atl1c_trans_normal);
+
+ handled = IRQ_HANDLED;
+ /* check if PCIE PHY Link down */
+ if (status & ISR_ERROR) {
+ if (netif_msg_hw(adapter))
+ dev_err(&pdev->dev,
+ "atl1c hardware error (status = 0x%x)\n",
+ status & ISR_ERROR);
+ /* reset MAC */
+ adapter->work_event |= ATL1C_WORK_EVENT_RESET;
+ schedule_work(&adapter->common_task);
+ return IRQ_HANDLED;
+ }
+
+ if (status & ISR_OVER)
+ if (netif_msg_intr(adapter))
+ dev_warn(&pdev->dev,
+ "TX/RX overflow (status = 0x%x)\n",
+ status & ISR_OVER);
+
+ /* link event */
+ if (status & (ISR_GPHY | ISR_MANUAL)) {
+ netdev->stats.tx_carrier_errors++;
+ atl1c_link_chg_event(adapter);
+ break;
+ }
+
+ } while (--max_ints > 0);
+ /* re-enable Interrupt*/
+ AT_WRITE_REG(&adapter->hw, REG_ISR, 0);
+ return handled;
+}
+
+static inline void atl1c_rx_checksum(struct atl1c_adapter *adapter,
+ struct sk_buff *skb, struct atl1c_recv_ret_status *prrs)
+{
+ /*
+ * The pid field in RRS in not correct sometimes, so we
+ * cannot figure out if the packet is fragmented or not,
+ * so we tell the KERNEL CHECKSUM_NONE
+ */
+ skb_checksum_none_assert(skb);
+}
+
+static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter, const int ringid)
+{
+ struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring[ringid];
+ struct pci_dev *pdev = adapter->pdev;
+ struct atl1c_buffer *buffer_info, *next_info;
+ struct sk_buff *skb;
+ void *vir_addr = NULL;
+ u16 num_alloc = 0;
+ u16 rfd_next_to_use, next_next;
+ struct atl1c_rx_free_desc *rfd_desc;
+
+ next_next = rfd_next_to_use = rfd_ring->next_to_use;
+ if (++next_next == rfd_ring->count)
+ next_next = 0;
+ buffer_info = &rfd_ring->buffer_info[rfd_next_to_use];
+ next_info = &rfd_ring->buffer_info[next_next];
+
+ while (next_info->flags & ATL1C_BUFFER_FREE) {
+ rfd_desc = ATL1C_RFD_DESC(rfd_ring, rfd_next_to_use);
+
+ skb = dev_alloc_skb(adapter->rx_buffer_len);
+ if (unlikely(!skb)) {
+ if (netif_msg_rx_err(adapter))
+ dev_warn(&pdev->dev, "alloc rx buffer failed\n");
+ break;
+ }
+
+ /*
+ * Make buffer alignment 2 beyond a 16 byte boundary
+ * this will result in a 16 byte aligned IP header after
+ * the 14 byte MAC header is removed
+ */
+ vir_addr = skb->data;
+ ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
+ buffer_info->skb = skb;
+ buffer_info->length = adapter->rx_buffer_len;
+ buffer_info->dma = pci_map_single(pdev, vir_addr,
+ buffer_info->length,
+ PCI_DMA_FROMDEVICE);
+ ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE,
+ ATL1C_PCIMAP_FROMDEVICE);
+ rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
+ rfd_next_to_use = next_next;
+ if (++next_next == rfd_ring->count)
+ next_next = 0;
+ buffer_info = &rfd_ring->buffer_info[rfd_next_to_use];
+ next_info = &rfd_ring->buffer_info[next_next];
+ num_alloc++;
+ }
+
+ if (num_alloc) {
+ /* TODO: update mailbox here */
+ wmb();
+ rfd_ring->next_to_use = rfd_next_to_use;
+ AT_WRITE_REG(&adapter->hw, atl1c_rfd_prod_idx_regs[ringid],
+ rfd_ring->next_to_use & MB_RFDX_PROD_IDX_MASK);
+ }
+
+ return num_alloc;
+}
+
+static void atl1c_clean_rrd(struct atl1c_rrd_ring *rrd_ring,
+ struct atl1c_recv_ret_status *rrs, u16 num)
+{
+ u16 i;
+ /* the relationship between rrd and rfd is one map one */
+ for (i = 0; i < num; i++, rrs = ATL1C_RRD_DESC(rrd_ring,
+ rrd_ring->next_to_clean)) {
+ rrs->word3 &= ~RRS_RXD_UPDATED;
+ if (++rrd_ring->next_to_clean == rrd_ring->count)
+ rrd_ring->next_to_clean = 0;
+ }
+}
+
+static void atl1c_clean_rfd(struct atl1c_rfd_ring *rfd_ring,
+ struct atl1c_recv_ret_status *rrs, u16 num)
+{
+ u16 i;
+ u16 rfd_index;
+ struct atl1c_buffer *buffer_info = rfd_ring->buffer_info;
+
+ rfd_index = (rrs->word0 >> RRS_RX_RFD_INDEX_SHIFT) &
+ RRS_RX_RFD_INDEX_MASK;
+ for (i = 0; i < num; i++) {
+ buffer_info[rfd_index].skb = NULL;
+ ATL1C_SET_BUFFER_STATE(&buffer_info[rfd_index],
+ ATL1C_BUFFER_FREE);
+ if (++rfd_index == rfd_ring->count)
+ rfd_index = 0;
+ }
+ rfd_ring->next_to_clean = rfd_index;
+}
+
+static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, u8 que,
+ int *work_done, int work_to_do)
+{
+ u16 rfd_num, rfd_index;
+ u16 count = 0;
+ u16 length;
+ struct pci_dev *pdev = adapter->pdev;
+ struct net_device *netdev = adapter->netdev;
+ struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring[que];
+ struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring[que];
+ struct sk_buff *skb;
+ struct atl1c_recv_ret_status *rrs;
+ struct atl1c_buffer *buffer_info;
+
+ while (1) {
+ if (*work_done >= work_to_do)
+ break;
+ rrs = ATL1C_RRD_DESC(rrd_ring, rrd_ring->next_to_clean);
+ if (likely(RRS_RXD_IS_VALID(rrs->word3))) {
+ rfd_num = (rrs->word0 >> RRS_RX_RFD_CNT_SHIFT) &
+ RRS_RX_RFD_CNT_MASK;
+ if (unlikely(rfd_num != 1))
+ /* TODO support mul rfd*/
+ if (netif_msg_rx_err(adapter))
+ dev_warn(&pdev->dev,
+ "Multi rfd not support yet!\n");
+ goto rrs_checked;
+ } else {
+ break;
+ }
+rrs_checked:
+ atl1c_clean_rrd(rrd_ring, rrs, rfd_num);
+ if (rrs->word3 & (RRS_RX_ERR_SUM | RRS_802_3_LEN_ERR)) {
+ atl1c_clean_rfd(rfd_ring, rrs, rfd_num);
+ if (netif_msg_rx_err(adapter))
+ dev_warn(&pdev->dev,
+ "wrong packet! rrs word3 is %x\n",
+ rrs->word3);
+ continue;
+ }
+
+ length = le16_to_cpu((rrs->word3 >> RRS_PKT_SIZE_SHIFT) &
+ RRS_PKT_SIZE_MASK);
+ /* Good Receive */
+ if (likely(rfd_num == 1)) {
+ rfd_index = (rrs->word0 >> RRS_RX_RFD_INDEX_SHIFT) &
+ RRS_RX_RFD_INDEX_MASK;
+ buffer_info = &rfd_ring->buffer_info[rfd_index];
+ pci_unmap_single(pdev, buffer_info->dma,
+ buffer_info->length, PCI_DMA_FROMDEVICE);
+ skb = buffer_info->skb;
+ } else {
+ /* TODO */
+ if (netif_msg_rx_err(adapter))
+ dev_warn(&pdev->dev,
+ "Multi rfd not support yet!\n");
+ break;
+ }
+ atl1c_clean_rfd(rfd_ring, rrs, rfd_num);
+ skb_put(skb, length - ETH_FCS_LEN);
+ skb->protocol = eth_type_trans(skb, netdev);
+ atl1c_rx_checksum(adapter, skb, rrs);
+ if (rrs->word3 & RRS_VLAN_INS) {
+ u16 vlan;
+
+ AT_TAG_TO_VLAN(rrs->vlan_tag, vlan);
+ vlan = le16_to_cpu(vlan);
+ __vlan_hwaccel_put_tag(skb, vlan);
+ }
+ netif_receive_skb(skb);
+
+ (*work_done)++;
+ count++;
+ }
+ if (count)
+ atl1c_alloc_rx_buffer(adapter, que);
+}
+
+/*
+ * atl1c_clean - NAPI Rx polling callback
+ * @adapter: board private structure
+ */
+static int atl1c_clean(struct napi_struct *napi, int budget)
+{
+ struct atl1c_adapter *adapter =
+ container_of(napi, struct atl1c_adapter, napi);
+ int work_done = 0;
+
+ /* Keep link state information with original netdev */
+ if (!netif_carrier_ok(adapter->netdev))
+ goto quit_polling;
+ /* just enable one RXQ */
+ atl1c_clean_rx_irq(adapter, 0, &work_done, budget);
+
+ if (work_done < budget) {
+quit_polling:
+ napi_complete(napi);
+ adapter->hw.intr_mask |= ISR_RX_PKT;
+ AT_WRITE_REG(&adapter->hw, REG_IMR, adapter->hw.intr_mask);
+ }
+ return work_done;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+
+/*
+ * 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.
+ */
+static void atl1c_netpoll(struct net_device *netdev)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+
+ disable_irq(adapter->pdev->irq);
+ atl1c_intr(adapter->pdev->irq, netdev);
+ enable_irq(adapter->pdev->irq);
+}
+#endif
+
+static inline u16 atl1c_tpd_avail(struct atl1c_adapter *adapter, enum atl1c_trans_queue type)
+{
+ struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type];
+ u16 next_to_use = 0;
+ u16 next_to_clean = 0;
+
+ next_to_clean = atomic_read(&tpd_ring->next_to_clean);
+ next_to_use = tpd_ring->next_to_use;
+
+ return (u16)(next_to_clean > next_to_use) ?
+ (next_to_clean - next_to_use - 1) :
+ (tpd_ring->count + next_to_clean - next_to_use - 1);
+}
+
+/*
+ * get next usable tpd
+ * Note: should call atl1c_tdp_avail to make sure
+ * there is enough tpd to use
+ */
+static struct atl1c_tpd_desc *atl1c_get_tpd(struct atl1c_adapter *adapter,
+ enum atl1c_trans_queue type)
+{
+ struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type];
+ struct atl1c_tpd_desc *tpd_desc;
+ u16 next_to_use = 0;
+
+ next_to_use = tpd_ring->next_to_use;
+ if (++tpd_ring->next_to_use == tpd_ring->count)
+ tpd_ring->next_to_use = 0;
+ tpd_desc = ATL1C_TPD_DESC(tpd_ring, next_to_use);
+ memset(tpd_desc, 0, sizeof(struct atl1c_tpd_desc));
+ return tpd_desc;
+}
+
+static struct atl1c_buffer *
+atl1c_get_tx_buffer(struct atl1c_adapter *adapter, struct atl1c_tpd_desc *tpd)
+{
+ struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring;
+
+ return &tpd_ring->buffer_info[tpd -
+ (struct atl1c_tpd_desc *)tpd_ring->desc];
+}
+
+/* Calculate the transmit packet descript needed*/
+static u16 atl1c_cal_tpd_req(const struct sk_buff *skb)
+{
+ u16 tpd_req;
+ u16 proto_hdr_len = 0;
+
+ tpd_req = skb_shinfo(skb)->nr_frags + 1;
+
+ if (skb_is_gso(skb)) {
+ proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+ if (proto_hdr_len < skb_headlen(skb))
+ tpd_req++;
+ if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)
+ tpd_req++;
+ }
+ return tpd_req;
+}
+
+static int atl1c_tso_csum(struct atl1c_adapter *adapter,
+ struct sk_buff *skb,
+ struct atl1c_tpd_desc **tpd,
+ enum atl1c_trans_queue type)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ u8 hdr_len;
+ u32 real_len;
+ unsigned short offload_type;
+ int err;
+
+ if (skb_is_gso(skb)) {
+ if (skb_header_cloned(skb)) {
+ err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+ if (unlikely(err))
+ return -1;
+ }
+ offload_type = skb_shinfo(skb)->gso_type;
+
+ if (offload_type & SKB_GSO_TCPV4) {
+ real_len = (((unsigned char *)ip_hdr(skb) - skb->data)
+ + ntohs(ip_hdr(skb)->tot_len));
+
+ if (real_len < skb->len)
+ pskb_trim(skb, real_len);
+
+ hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb));
+ if (unlikely(skb->len == hdr_len)) {
+ /* only xsum need */
+ if (netif_msg_tx_queued(adapter))
+ dev_warn(&pdev->dev,
+ "IPV4 tso with zero data??\n");
+ goto check_sum;
+ } else {
+ ip_hdr(skb)->check = 0;
+ tcp_hdr(skb)->check = ~csum_tcpudp_magic(
+ ip_hdr(skb)->saddr,
+ ip_hdr(skb)->daddr,
+ 0, IPPROTO_TCP, 0);
+ (*tpd)->word1 |= 1 << TPD_IPV4_PACKET_SHIFT;
+ }
+ }
+
+ if (offload_type & SKB_GSO_TCPV6) {
+ struct atl1c_tpd_ext_desc *etpd =
+ *(struct atl1c_tpd_ext_desc **)(tpd);
+
+ memset(etpd, 0, sizeof(struct atl1c_tpd_ext_desc));
+ *tpd = atl1c_get_tpd(adapter, type);
+ ipv6_hdr(skb)->payload_len = 0;
+ /* check payload == 0 byte ? */
+ hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb));
+ if (unlikely(skb->len == hdr_len)) {
+ /* only xsum need */
+ if (netif_msg_tx_queued(adapter))
+ dev_warn(&pdev->dev,
+ "IPV6 tso with zero data??\n");
+ goto check_sum;
+ } else
+ tcp_hdr(skb)->check = ~csum_ipv6_magic(
+ &ipv6_hdr(skb)->saddr,
+ &ipv6_hdr(skb)->daddr,
+ 0, IPPROTO_TCP, 0);
+ etpd->word1 |= 1 << TPD_LSO_EN_SHIFT;
+ etpd->word1 |= 1 << TPD_LSO_VER_SHIFT;
+ etpd->pkt_len = cpu_to_le32(skb->len);
+ (*tpd)->word1 |= 1 << TPD_LSO_VER_SHIFT;
+ }
+
+ (*tpd)->word1 |= 1 << TPD_LSO_EN_SHIFT;
+ (*tpd)->word1 |= (skb_transport_offset(skb) & TPD_TCPHDR_OFFSET_MASK) <<
+ TPD_TCPHDR_OFFSET_SHIFT;
+ (*tpd)->word1 |= (skb_shinfo(skb)->gso_size & TPD_MSS_MASK) <<
+ TPD_MSS_SHIFT;
+ return 0;
+ }
+
+check_sum:
+ if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
+ u8 css, cso;
+ cso = skb_checksum_start_offset(skb);
+
+ if (unlikely(cso & 0x1)) {
+ if (netif_msg_tx_err(adapter))
+ dev_err(&adapter->pdev->dev,
+ "payload offset should not an event number\n");
+ return -1;
+ } else {
+ css = cso + skb->csum_offset;
+
+ (*tpd)->word1 |= ((cso >> 1) & TPD_PLOADOFFSET_MASK) <<
+ TPD_PLOADOFFSET_SHIFT;
+ (*tpd)->word1 |= ((css >> 1) & TPD_CCSUM_OFFSET_MASK) <<
+ TPD_CCSUM_OFFSET_SHIFT;
+ (*tpd)->word1 |= 1 << TPD_CCSUM_EN_SHIFT;
+ }
+ }
+ return 0;
+}
+
+static void atl1c_tx_map(struct atl1c_adapter *adapter,
+ struct sk_buff *skb, struct atl1c_tpd_desc *tpd,
+ enum atl1c_trans_queue type)
+{
+ struct atl1c_tpd_desc *use_tpd = NULL;
+ struct atl1c_buffer *buffer_info = NULL;
+ u16 buf_len = skb_headlen(skb);
+ u16 map_len = 0;
+ u16 mapped_len = 0;
+ u16 hdr_len = 0;
+ u16 nr_frags;
+ u16 f;
+ int tso;
+
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ tso = (tpd->word1 >> TPD_LSO_EN_SHIFT) & TPD_LSO_EN_MASK;
+ if (tso) {
+ /* TSO */
+ map_len = hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+ use_tpd = tpd;
+
+ buffer_info = atl1c_get_tx_buffer(adapter, use_tpd);
+ buffer_info->length = map_len;
+ buffer_info->dma = pci_map_single(adapter->pdev,
+ skb->data, hdr_len, PCI_DMA_TODEVICE);
+ ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
+ ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE,
+ ATL1C_PCIMAP_TODEVICE);
+ mapped_len += map_len;
+ use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma);
+ use_tpd->buffer_len = cpu_to_le16(buffer_info->length);
+ }
+
+ if (mapped_len < buf_len) {
+ /* mapped_len == 0, means we should use the first tpd,
+ which is given by caller */
+ if (mapped_len == 0)
+ use_tpd = tpd;
+ else {
+ use_tpd = atl1c_get_tpd(adapter, type);
+ memcpy(use_tpd, tpd, sizeof(struct atl1c_tpd_desc));
+ }
+ buffer_info = atl1c_get_tx_buffer(adapter, use_tpd);
+ buffer_info->length = buf_len - mapped_len;
+ buffer_info->dma =
+ pci_map_single(adapter->pdev, skb->data + mapped_len,
+ buffer_info->length, PCI_DMA_TODEVICE);
+ ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
+ ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE,
+ ATL1C_PCIMAP_TODEVICE);
+ use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma);
+ use_tpd->buffer_len = cpu_to_le16(buffer_info->length);
+ }
+
+ for (f = 0; f < nr_frags; f++) {
+ struct skb_frag_struct *frag;
+
+ frag = &skb_shinfo(skb)->frags[f];
+
+ use_tpd = atl1c_get_tpd(adapter, type);
+ memcpy(use_tpd, tpd, sizeof(struct atl1c_tpd_desc));
+
+ buffer_info = atl1c_get_tx_buffer(adapter, use_tpd);
+ buffer_info->length = skb_frag_size(frag);
+ buffer_info->dma = skb_frag_dma_map(&adapter->pdev->dev,
+ frag, 0,
+ buffer_info->length,
+ DMA_TO_DEVICE);
+ ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
+ ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_PAGE,
+ ATL1C_PCIMAP_TODEVICE);
+ use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma);
+ use_tpd->buffer_len = cpu_to_le16(buffer_info->length);
+ }
+
+ /* The last tpd */
+ use_tpd->word1 |= 1 << TPD_EOP_SHIFT;
+ /* The last buffer info contain the skb address,
+ so it will be free after unmap */
+ buffer_info->skb = skb;
+}
+
+static void atl1c_tx_queue(struct atl1c_adapter *adapter, struct sk_buff *skb,
+ struct atl1c_tpd_desc *tpd, enum atl1c_trans_queue type)
+{
+ struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type];
+ u32 prod_data;
+
+ AT_READ_REG(&adapter->hw, REG_MB_PRIO_PROD_IDX, &prod_data);
+ switch (type) {
+ case atl1c_trans_high:
+ prod_data &= 0xFFFF0000;
+ prod_data |= tpd_ring->next_to_use & 0xFFFF;
+ break;
+ case atl1c_trans_normal:
+ prod_data &= 0x0000FFFF;
+ prod_data |= (tpd_ring->next_to_use & 0xFFFF) << 16;
+ break;
+ default:
+ break;
+ }
+ wmb();
+ AT_WRITE_REG(&adapter->hw, REG_MB_PRIO_PROD_IDX, prod_data);
+}
+
+static netdev_tx_t atl1c_xmit_frame(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ unsigned long flags;
+ u16 tpd_req = 1;
+ struct atl1c_tpd_desc *tpd;
+ enum atl1c_trans_queue type = atl1c_trans_normal;
+
+ if (test_bit(__AT_DOWN, &adapter->flags)) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ tpd_req = atl1c_cal_tpd_req(skb);
+ if (!spin_trylock_irqsave(&adapter->tx_lock, flags)) {
+ if (netif_msg_pktdata(adapter))
+ dev_info(&adapter->pdev->dev, "tx locked\n");
+ return NETDEV_TX_LOCKED;
+ }
+ if (skb->mark == 0x01)
+ type = atl1c_trans_high;
+ else
+ type = atl1c_trans_normal;
+
+ if (atl1c_tpd_avail(adapter, type) < tpd_req) {
+ /* no enough descriptor, just stop queue */
+ netif_stop_queue(netdev);
+ spin_unlock_irqrestore(&adapter->tx_lock, flags);
+ return NETDEV_TX_BUSY;
+ }
+
+ tpd = atl1c_get_tpd(adapter, type);
+
+ /* do TSO and check sum */
+ if (atl1c_tso_csum(adapter, skb, &tpd, type) != 0) {
+ spin_unlock_irqrestore(&adapter->tx_lock, flags);
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ if (unlikely(vlan_tx_tag_present(skb))) {
+ u16 vlan = vlan_tx_tag_get(skb);
+ __le16 tag;
+
+ vlan = cpu_to_le16(vlan);
+ AT_VLAN_TO_TAG(vlan, tag);
+ tpd->word1 |= 1 << TPD_INS_VTAG_SHIFT;
+ tpd->vlan_tag = tag;
+ }
+
+ if (skb_network_offset(skb) != ETH_HLEN)
+ tpd->word1 |= 1 << TPD_ETH_TYPE_SHIFT; /* Ethernet frame */
+
+ atl1c_tx_map(adapter, skb, tpd, type);
+ atl1c_tx_queue(adapter, skb, tpd, type);
+
+ spin_unlock_irqrestore(&adapter->tx_lock, flags);
+ return NETDEV_TX_OK;
+}
+
+static void atl1c_free_irq(struct atl1c_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+
+ free_irq(adapter->pdev->irq, netdev);
+
+ if (adapter->have_msi)
+ pci_disable_msi(adapter->pdev);
+}
+
+static int atl1c_request_irq(struct atl1c_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ struct net_device *netdev = adapter->netdev;
+ int flags = 0;
+ int err = 0;
+
+ adapter->have_msi = true;
+ err = pci_enable_msi(adapter->pdev);
+ if (err) {
+ if (netif_msg_ifup(adapter))
+ dev_err(&pdev->dev,
+ "Unable to allocate MSI interrupt Error: %d\n",
+ err);
+ adapter->have_msi = false;
+ } else
+ netdev->irq = pdev->irq;
+
+ if (!adapter->have_msi)
+ flags |= IRQF_SHARED;
+ err = request_irq(adapter->pdev->irq, atl1c_intr, flags,
+ netdev->name, netdev);
+ if (err) {
+ if (netif_msg_ifup(adapter))
+ dev_err(&pdev->dev,
+ "Unable to allocate interrupt Error: %d\n",
+ err);
+ if (adapter->have_msi)
+ pci_disable_msi(adapter->pdev);
+ return err;
+ }
+ if (netif_msg_ifup(adapter))
+ dev_dbg(&pdev->dev, "atl1c_request_irq OK\n");
+ return err;
+}
+
+static int atl1c_up(struct atl1c_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ int num;
+ int err;
+ int i;
+
+ netif_carrier_off(netdev);
+ atl1c_init_ring_ptrs(adapter);
+ atl1c_set_multi(netdev);
+ atl1c_restore_vlan(adapter);
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ num = atl1c_alloc_rx_buffer(adapter, i);
+ if (unlikely(num == 0)) {
+ err = -ENOMEM;
+ goto err_alloc_rx;
+ }
+ }
+
+ if (atl1c_configure(adapter)) {
+ err = -EIO;
+ goto err_up;
+ }
+
+ err = atl1c_request_irq(adapter);
+ if (unlikely(err))
+ goto err_up;
+
+ clear_bit(__AT_DOWN, &adapter->flags);
+ napi_enable(&adapter->napi);
+ atl1c_irq_enable(adapter);
+ atl1c_check_link_status(adapter);
+ netif_start_queue(netdev);
+ return err;
+
+err_up:
+err_alloc_rx:
+ atl1c_clean_rx_ring(adapter);
+ return err;
+}
+
+static void atl1c_down(struct atl1c_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+
+ atl1c_del_timer(adapter);
+ adapter->work_event = 0; /* clear all event */
+ /* signal that we're down so the interrupt handler does not
+ * reschedule our watchdog timer */
+ set_bit(__AT_DOWN, &adapter->flags);
+ netif_carrier_off(netdev);
+ napi_disable(&adapter->napi);
+ atl1c_irq_disable(adapter);
+ atl1c_free_irq(adapter);
+ /* reset MAC to disable all RX/TX */
+ atl1c_reset_mac(&adapter->hw);
+ msleep(1);
+
+ adapter->link_speed = SPEED_0;
+ adapter->link_duplex = -1;
+ atl1c_clean_tx_ring(adapter, atl1c_trans_normal);
+ atl1c_clean_tx_ring(adapter, atl1c_trans_high);
+ atl1c_clean_rx_ring(adapter);
+}
+
+/*
+ * atl1c_open - Called when a network interface is made active
+ * @netdev: network interface device structure
+ *
+ * Returns 0 on success, negative value on failure
+ *
+ * The open entry point is called when a network interface is made
+ * active by the system (IFF_UP). At this point all resources needed
+ * for transmit and receive operations are allocated, the interrupt
+ * handler is registered with the OS, the watchdog timer is started,
+ * and the stack is notified that the interface is ready.
+ */
+static int atl1c_open(struct net_device *netdev)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ int err;
+
+ /* disallow open during test */
+ if (test_bit(__AT_TESTING, &adapter->flags))
+ return -EBUSY;
+
+ /* allocate rx/tx dma buffer & descriptors */
+ err = atl1c_setup_ring_resources(adapter);
+ if (unlikely(err))
+ return err;
+
+ err = atl1c_up(adapter);
+ if (unlikely(err))
+ goto err_up;
+
+ if (adapter->hw.ctrl_flags & ATL1C_FPGA_VERSION) {
+ u32 phy_data;
+
+ AT_READ_REG(&adapter->hw, REG_MDIO_CTRL, &phy_data);
+ phy_data |= MDIO_AP_EN;
+ AT_WRITE_REG(&adapter->hw, REG_MDIO_CTRL, phy_data);
+ }
+ return 0;
+
+err_up:
+ atl1c_free_irq(adapter);
+ atl1c_free_ring_resources(adapter);
+ atl1c_reset_mac(&adapter->hw);
+ return err;
+}
+
+/*
+ * atl1c_close - Disables a network interface
+ * @netdev: network interface device structure
+ *
+ * Returns 0, this is not allowed to fail
+ *
+ * The close entry point is called when an interface is de-activated
+ * by the OS. The hardware is still under the drivers control, but
+ * needs to be disabled. A global MAC reset is issued to stop the
+ * hardware, and all transmit and receive resources are freed.
+ */
+static int atl1c_close(struct net_device *netdev)
+{
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+
+ WARN_ON(test_bit(__AT_RESETTING, &adapter->flags));
+ atl1c_down(adapter);
+ atl1c_free_ring_resources(adapter);
+ return 0;
+}
+
+static int atl1c_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+ struct atl1c_hw *hw = &adapter->hw;
+ u32 mac_ctrl_data = 0;
+ u32 master_ctrl_data = 0;
+ u32 wol_ctrl_data = 0;
+ u16 mii_intr_status_data = 0;
+ u32 wufc = adapter->wol;
+
+ atl1c_disable_l0s_l1(hw);
+ if (netif_running(netdev)) {
+ WARN_ON(test_bit(__AT_RESETTING, &adapter->flags));
+ atl1c_down(adapter);
+ }
+ netif_device_detach(netdev);
+
+ if (wufc)
+ if (atl1c_phy_power_saving(hw) != 0)
+ dev_dbg(&pdev->dev, "phy power saving failed");
+
+ AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl_data);
+ AT_READ_REG(hw, REG_MAC_CTRL, &mac_ctrl_data);
+
+ master_ctrl_data &= ~MASTER_CTRL_CLK_SEL_DIS;
+ mac_ctrl_data &= ~(MAC_CTRL_PRMLEN_MASK << MAC_CTRL_PRMLEN_SHIFT);
+ mac_ctrl_data |= (((u32)adapter->hw.preamble_len &
+ MAC_CTRL_PRMLEN_MASK) <<
+ MAC_CTRL_PRMLEN_SHIFT);
+ mac_ctrl_data &= ~(MAC_CTRL_SPEED_MASK << MAC_CTRL_SPEED_SHIFT);
+ mac_ctrl_data &= ~MAC_CTRL_DUPLX;
+
+ if (wufc) {
+ mac_ctrl_data |= MAC_CTRL_RX_EN;
+ if (adapter->link_speed == SPEED_1000 ||
+ adapter->link_speed == SPEED_0) {
+ mac_ctrl_data |= atl1c_mac_speed_1000 <<
+ MAC_CTRL_SPEED_SHIFT;
+ mac_ctrl_data |= MAC_CTRL_DUPLX;
+ } else
+ mac_ctrl_data |= atl1c_mac_speed_10_100 <<
+ MAC_CTRL_SPEED_SHIFT;
+
+ if (adapter->link_duplex == DUPLEX_FULL)
+ mac_ctrl_data |= MAC_CTRL_DUPLX;
+
+ /* turn on magic packet wol */
+ if (wufc & AT_WUFC_MAG)
+ wol_ctrl_data |= WOL_MAGIC_EN | WOL_MAGIC_PME_EN;
+
+ if (wufc & AT_WUFC_LNKC) {
+ wol_ctrl_data |= WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN;
+ /* only link up can wake up */
+ if (atl1c_write_phy_reg(hw, MII_IER, IER_LINK_UP) != 0) {
+ dev_dbg(&pdev->dev, "%s: read write phy "
+ "register failed.\n",
+ atl1c_driver_name);
+ }
+ }
+ /* clear phy interrupt */
+ atl1c_read_phy_reg(hw, MII_ISR, &mii_intr_status_data);
+ /* Config MAC Ctrl register */
+ __atl1c_vlan_mode(netdev->features, &mac_ctrl_data);
+
+ /* magic packet maybe Broadcast&multicast&Unicast frame */
+ if (wufc & AT_WUFC_MAG)
+ mac_ctrl_data |= MAC_CTRL_BC_EN;
+
+ dev_dbg(&pdev->dev,
+ "%s: suspend MAC=0x%x\n",
+ atl1c_driver_name, mac_ctrl_data);
+ AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data);
+ AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl_data);
+ AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data);
+
+ AT_WRITE_REG(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT |
+ GPHY_CTRL_EXT_RESET);
+ } else {
+ AT_WRITE_REG(hw, REG_GPHY_CTRL, GPHY_CTRL_POWER_SAVING);
+ master_ctrl_data |= MASTER_CTRL_CLK_SEL_DIS;
+ mac_ctrl_data |= atl1c_mac_speed_10_100 << MAC_CTRL_SPEED_SHIFT;
+ mac_ctrl_data |= MAC_CTRL_DUPLX;
+ AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data);
+ AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data);
+ AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
+ hw->phy_configured = false; /* re-init PHY when resume */
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int atl1c_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+
+ AT_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0);
+ atl1c_reset_pcie(&adapter->hw, ATL1C_PCIE_L0S_L1_DISABLE |
+ ATL1C_PCIE_PHY_RESET);
+
+ atl1c_phy_reset(&adapter->hw);
+ atl1c_reset_mac(&adapter->hw);
+ atl1c_phy_init(&adapter->hw);
+
+#if 0
+ AT_READ_REG(&adapter->hw, REG_PM_CTRLSTAT, &pm_data);
+ pm_data &= ~PM_CTRLSTAT_PME_EN;
+ AT_WRITE_REG(&adapter->hw, REG_PM_CTRLSTAT, pm_data);
+#endif
+
+ netif_device_attach(netdev);
+ if (netif_running(netdev))
+ atl1c_up(adapter);
+
+ return 0;
+}
+#endif
+
+static void atl1c_shutdown(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+
+ atl1c_suspend(&pdev->dev);
+ pci_wake_from_d3(pdev, adapter->wol);
+ pci_set_power_state(pdev, PCI_D3hot);
+}
+
+static const struct net_device_ops atl1c_netdev_ops = {
+ .ndo_open = atl1c_open,
+ .ndo_stop = atl1c_close,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_start_xmit = atl1c_xmit_frame,
+ .ndo_set_mac_address = atl1c_set_mac_addr,
+ .ndo_set_rx_mode = atl1c_set_multi,
+ .ndo_change_mtu = atl1c_change_mtu,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39))
+ .ndo_fix_features = atl1c_fix_features,
+ .ndo_set_features = atl1c_set_features,
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */
+ .ndo_do_ioctl = atl1c_ioctl,
+ .ndo_tx_timeout = atl1c_tx_timeout,
+ .ndo_get_stats = atl1c_get_stats,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = atl1c_netpoll,
+#endif
+};
+
+static int atl1c_init_netdev(struct net_device *netdev, struct pci_dev *pdev)
+{
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+ pci_set_drvdata(pdev, netdev);
+
+ netdev->irq = pdev->irq;
+ netdev_attach_ops(netdev, &atl1c_netdev_ops);
+ netdev->watchdog_timeo = AT_TX_WATCHDOG;
+ atl1c_set_ethtool_ops(netdev);
+
+ /* TODO: add when ready */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39))
+ netdev->hw_features = NETIF_F_SG |
+ NETIF_F_HW_CSUM |
+ NETIF_F_HW_VLAN_RX |
+ NETIF_F_TSO |
+ NETIF_F_TSO6;
+ netdev->features = netdev->hw_features |
+ NETIF_F_HW_VLAN_TX;
+#else
+ netdev->features = NETIF_F_SG |
+ NETIF_F_HW_CSUM |
+ NETIF_F_HW_VLAN_TX |
+ NETIF_F_HW_VLAN_RX |
+ NETIF_F_TSO |
+ NETIF_F_TSO6;
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */
+ return 0;
+}
+
+/*
+ * atl1c_probe - Device Initialization Routine
+ * @pdev: PCI device information struct
+ * @ent: entry in atl1c_pci_tbl
+ *
+ * Returns 0 on success, negative on failure
+ *
+ * atl1c_probe initializes an adapter identified by a pci_dev structure.
+ * The OS initialization, configuring of the adapter private structure,
+ * and a hardware reset occur.
+ */
+static int __devinit atl1c_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *netdev;
+ struct atl1c_adapter *adapter;
+ static int cards_found;
+
+ int err = 0;
+
+ /* enable device (incl. PCI PM wakeup and hotplug setup) */
+ err = pci_enable_device_mem(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "cannot enable PCI device\n");
+ return err;
+ }
+
+ /*
+ * The atl1c chip can DMA to 64-bit addresses, but it uses a single
+ * shared register for the high 32 bits, so only a single, aligned,
+ * 4 GB physical address range can be used at a time.
+ *
+ * Supporting 64-bit DMA on this hardware is more trouble than it's
+ * worth. It is far easier to limit to 32-bit DMA than update
+ * various kernel subsystems to support the mechanics required by a
+ * fixed-high-32-bit system.
+ */
+ if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) ||
+ (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) != 0)) {
+ dev_err(&pdev->dev, "No usable DMA configuration,aborting\n");
+ goto err_dma;
+ }
+
+ err = pci_request_regions(pdev, atl1c_driver_name);
+ if (err) {
+ dev_err(&pdev->dev, "cannot obtain PCI resources\n");
+ goto err_pci_reg;
+ }
+
+ pci_set_master(pdev);
+
+ netdev = alloc_etherdev(sizeof(struct atl1c_adapter));
+ if (netdev == NULL) {
+ err = -ENOMEM;
+ dev_err(&pdev->dev, "etherdev alloc failed\n");
+ goto err_alloc_etherdev;
+ }
+
+ err = atl1c_init_netdev(netdev, pdev);
+ if (err) {
+ dev_err(&pdev->dev, "init netdevice failed\n");
+ goto err_init_netdev;
+ }
+ adapter = netdev_priv(netdev);
+ adapter->bd_number = cards_found;
+ adapter->netdev = netdev;
+ adapter->pdev = pdev;
+ adapter->hw.adapter = adapter;
+ adapter->msg_enable = netif_msg_init(-1, atl1c_default_msg);
+ adapter->hw.hw_addr = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+ if (!adapter->hw.hw_addr) {
+ err = -EIO;
+ dev_err(&pdev->dev, "cannot map device registers\n");
+ goto err_ioremap;
+ }
+ netdev->base_addr = (unsigned long)adapter->hw.hw_addr;
+
+ /* init mii data */
+ adapter->mii.dev = netdev;
+ adapter->mii.mdio_read = atl1c_mdio_read;
+ adapter->mii.mdio_write = atl1c_mdio_write;
+ adapter->mii.phy_id_mask = 0x1f;
+ adapter->mii.reg_num_mask = MDIO_REG_ADDR_MASK;
+ netif_napi_add(netdev, &adapter->napi, atl1c_clean, 64);
+ setup_timer(&adapter->phy_config_timer, atl1c_phy_config,
+ (unsigned long)adapter);
+ /* setup the private structure */
+ err = atl1c_sw_init(adapter);
+ if (err) {
+ dev_err(&pdev->dev, "net device private data init failed\n");
+ goto err_sw_init;
+ }
+ atl1c_reset_pcie(&adapter->hw, ATL1C_PCIE_L0S_L1_DISABLE |
+ ATL1C_PCIE_PHY_RESET);
+
+ /* Init GPHY as early as possible due to power saving issue */
+ atl1c_phy_reset(&adapter->hw);
+
+ err = atl1c_reset_mac(&adapter->hw);
+ if (err) {
+ err = -EIO;
+ goto err_reset;
+ }
+
+ /* reset the controller to
+ * put the device in a known good starting state */
+ err = atl1c_phy_init(&adapter->hw);
+ if (err) {
+ err = -EIO;
+ goto err_reset;
+ }
+ if (atl1c_read_mac_addr(&adapter->hw) != 0) {
+ err = -EIO;
+ dev_err(&pdev->dev, "get mac address failed\n");
+ goto err_eeprom;
+ }
+ memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len);
+ memcpy(netdev->perm_addr, adapter->hw.mac_addr, netdev->addr_len);
+ if (netif_msg_probe(adapter))
+ dev_dbg(&pdev->dev, "mac address : %pM\n",
+ adapter->hw.mac_addr);
+
+ atl1c_hw_set_mac_addr(&adapter->hw);
+ INIT_WORK(&adapter->common_task, atl1c_common_task);
+ adapter->work_event = 0;
+ err = register_netdev(netdev);
+ if (err) {
+ dev_err(&pdev->dev, "register netdevice failed\n");
+ goto err_register;
+ }
+
+ if (netif_msg_probe(adapter))
+ dev_info(&pdev->dev, "version %s\n", ATL1C_DRV_VERSION);
+ cards_found++;
+ return 0;
+
+err_reset:
+err_register:
+err_sw_init:
+err_eeprom:
+ iounmap(adapter->hw.hw_addr);
+err_init_netdev:
+err_ioremap:
+ free_netdev(netdev);
+err_alloc_etherdev:
+ pci_release_regions(pdev);
+err_pci_reg:
+err_dma:
+ pci_disable_device(pdev);
+ return err;
+}
+
+/*
+ * atl1c_remove - Device Removal Routine
+ * @pdev: PCI device information struct
+ *
+ * atl1c_remove is called by the PCI subsystem to alert the driver
+ * that it should release a PCI device. The could be caused by a
+ * Hot-Plug event, or because the driver is going to be removed from
+ * memory.
+ */
+static void __devexit atl1c_remove(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+
+ unregister_netdev(netdev);
+ atl1c_phy_disable(&adapter->hw);
+
+ iounmap(adapter->hw.hw_addr);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ free_netdev(netdev);
+}
+
+/*
+ * atl1c_io_error_detected - called when PCI error is detected
+ * @pdev: Pointer to PCI device
+ * @state: The current pci connection state
+ *
+ * This function is called after a PCI bus error affecting
+ * this device has been detected.
+ */
+static pci_ers_result_t atl1c_io_error_detected(struct pci_dev *pdev,
+ pci_channel_state_t state)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+
+ netif_device_detach(netdev);
+
+ if (state == pci_channel_io_perm_failure)
+ return PCI_ERS_RESULT_DISCONNECT;
+
+ if (netif_running(netdev))
+ atl1c_down(adapter);
+
+ pci_disable_device(pdev);
+
+ /* Request a slot slot reset. */
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/*
+ * atl1c_io_slot_reset - called after the pci bus has been reset.
+ * @pdev: Pointer to PCI device
+ *
+ * Restart the card from scratch, as if from a cold-boot. Implementation
+ * resembles the first-half of the e1000_resume routine.
+ */
+static pci_ers_result_t atl1c_io_slot_reset(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+
+ if (pci_enable_device(pdev)) {
+ if (netif_msg_hw(adapter))
+ dev_err(&pdev->dev,
+ "Cannot re-enable PCI device after reset\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+ pci_set_master(pdev);
+
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ pci_enable_wake(pdev, PCI_D3cold, 0);
+
+ atl1c_reset_mac(&adapter->hw);
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+/*
+ * atl1c_io_resume - called when traffic can start flowing again.
+ * @pdev: Pointer to PCI device
+ *
+ * This callback is called when the error recovery driver tells us that
+ * its OK to resume normal operation. Implementation resembles the
+ * second-half of the atl1c_resume routine.
+ */
+static void atl1c_io_resume(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1c_adapter *adapter = netdev_priv(netdev);
+
+ if (netif_running(netdev)) {
+ if (atl1c_up(adapter)) {
+ if (netif_msg_hw(adapter))
+ dev_err(&pdev->dev,
+ "Cannot bring device back up after reset\n");
+ return;
+ }
+ }
+
+ netif_device_attach(netdev);
+}
+
+static struct pci_error_handlers atl1c_err_handler = {
+ .error_detected = atl1c_io_error_detected,
+ .slot_reset = atl1c_io_slot_reset,
+ .resume = atl1c_io_resume,
+};
+
+static SIMPLE_DEV_PM_OPS(atl1c_pm_ops, atl1c_suspend, atl1c_resume);
+
+compat_pci_suspend(atl1c_suspend)
+compat_pci_resume(atl1c_resume)
+
+static struct pci_driver atl1c_driver = {
+ .name = atl1c_driver_name,
+ .id_table = atl1c_pci_tbl,
+ .probe = atl1c_probe,
+ .remove = __devexit_p(atl1c_remove),
+ .shutdown = atl1c_shutdown,
+ .err_handler = &atl1c_err_handler,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29))
+ .driver.pm = &atl1c_pm_ops,
+#elif defined(CONFIG_PM_SLEEP)
+ .suspend = atl1c_suspend_compat,
+ .resume = atl1c_resume_compat,
+#endif
+};
+
+/*
+ * atl1c_init_module - Driver Registration Routine
+ *
+ * atl1c_init_module is the first routine called when the driver is
+ * loaded. All it does is register with the PCI subsystem.
+ */
+static int __init atl1c_init_module(void)
+{
+ return pci_register_driver(&atl1c_driver);
+}
+
+/*
+ * atl1c_exit_module - Driver Exit Cleanup Routine
+ *
+ * atl1c_exit_module is called just before the driver is removed
+ * from memory.
+ */
+static void __exit atl1c_exit_module(void)
+{
+ pci_unregister_driver(&atl1c_driver);
+}
+
+module_init(atl1c_init_module);
+module_exit(atl1c_exit_module);
diff --git a/drivers/net/ethernet/atheros/atl1e/Makefile b/drivers/net/ethernet/atheros/atl1e/Makefile
new file mode 100644
index 0000000..bc11be8
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atl1e/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_ATL1E) += atl1e.o
+atl1e-objs += atl1e_main.o atl1e_hw.o atl1e_ethtool.o atl1e_param.o
diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e.h b/drivers/net/ethernet/atheros/atl1e/atl1e.h
new file mode 100644
index 0000000..829b5ad
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e.h
@@ -0,0 +1,509 @@
+/*
+ * Copyright(c) 2007 Atheros Corporation. All rights reserved.
+ * Copyright(c) 2007 xiong huang <xiong.huang@atheros.com>
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ */
+
+#ifndef _ATL1E_H_
+#define _ATL1E_H_
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/udp.h>
+#include <linux/mii.h>
+#include <linux/io.h>
+#include <linux/vmalloc.h>
+#include <linux/pagemap.h>
+#include <linux/tcp.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+#include <linux/workqueue.h>
+#include <net/checksum.h>
+#include <net/ip6_checksum.h>
+
+#include "atl1e_hw.h"
+
+#define PCI_REG_COMMAND 0x04 /* PCI Command Register */
+#define CMD_IO_SPACE 0x0001
+#define CMD_MEMORY_SPACE 0x0002
+#define CMD_BUS_MASTER 0x0004
+
+#define BAR_0 0
+#define BAR_1 1
+#define BAR_5 5
+
+/* Wake Up Filter Control */
+#define AT_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */
+#define AT_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */
+#define AT_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */
+#define AT_WUFC_MC 0x00000008 /* Multicast Wakeup Enable */
+#define AT_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */
+
+#define SPEED_0 0xffff
+#define HALF_DUPLEX 1
+#define FULL_DUPLEX 2
+
+/* Error Codes */
+#define AT_ERR_EEPROM 1
+#define AT_ERR_PHY 2
+#define AT_ERR_CONFIG 3
+#define AT_ERR_PARAM 4
+#define AT_ERR_MAC_TYPE 5
+#define AT_ERR_PHY_TYPE 6
+#define AT_ERR_PHY_SPEED 7
+#define AT_ERR_PHY_RES 8
+#define AT_ERR_TIMEOUT 9
+
+#define MAX_JUMBO_FRAME_SIZE 0x2000
+
+#define AT_VLAN_TAG_TO_TPD_TAG(_vlan, _tpd) \
+ _tpd = (((_vlan) << (4)) | (((_vlan) >> 13) & 7) |\
+ (((_vlan) >> 9) & 8))
+
+#define AT_TPD_TAG_TO_VLAN_TAG(_tpd, _vlan) \
+ _vlan = (((_tpd) >> 8) | (((_tpd) & 0x77) << 9) |\
+ (((_tdp) & 0x88) << 5))
+
+#define AT_MAX_RECEIVE_QUEUE 4
+#define AT_PAGE_NUM_PER_QUEUE 2
+
+#define AT_DMA_HI_ADDR_MASK 0xffffffff00000000ULL
+#define AT_DMA_LO_ADDR_MASK 0x00000000ffffffffULL
+
+#define AT_TX_WATCHDOG (5 * HZ)
+#define AT_MAX_INT_WORK 10
+#define AT_TWSI_EEPROM_TIMEOUT 100
+#define AT_HW_MAX_IDLE_DELAY 10
+#define AT_SUSPEND_LINK_TIMEOUT 28
+
+#define AT_REGS_LEN 75
+#define AT_EEPROM_LEN 512
+#define AT_ADV_MASK (ADVERTISE_10_HALF |\
+ ADVERTISE_10_FULL |\
+ ADVERTISE_100_HALF |\
+ ADVERTISE_100_FULL |\
+ ADVERTISE_1000_FULL)
+
+/* tpd word 2 */
+#define TPD_BUFLEN_MASK 0x3FFF
+#define TPD_BUFLEN_SHIFT 0
+#define TPD_DMAINT_MASK 0x0001
+#define TPD_DMAINT_SHIFT 14
+#define TPD_PKTNT_MASK 0x0001
+#define TPD_PKTINT_SHIFT 15
+#define TPD_VLANTAG_MASK 0xFFFF
+#define TPD_VLAN_SHIFT 16
+
+/* tpd word 3 bits 0:4 */
+#define TPD_EOP_MASK 0x0001
+#define TPD_EOP_SHIFT 0
+#define TPD_IP_VERSION_MASK 0x0001
+#define TPD_IP_VERSION_SHIFT 1 /* 0 : IPV4, 1 : IPV6 */
+#define TPD_INS_VL_TAG_MASK 0x0001
+#define TPD_INS_VL_TAG_SHIFT 2
+#define TPD_CC_SEGMENT_EN_MASK 0x0001
+#define TPD_CC_SEGMENT_EN_SHIFT 3
+#define TPD_SEGMENT_EN_MASK 0x0001
+#define TPD_SEGMENT_EN_SHIFT 4
+
+/* tdp word 3 bits 5:7 if ip version is 0 */
+#define TPD_IP_CSUM_MASK 0x0001
+#define TPD_IP_CSUM_SHIFT 5
+#define TPD_TCP_CSUM_MASK 0x0001
+#define TPD_TCP_CSUM_SHIFT 6
+#define TPD_UDP_CSUM_MASK 0x0001
+#define TPD_UDP_CSUM_SHIFT 7
+
+/* tdp word 3 bits 5:7 if ip version is 1 */
+#define TPD_V6_IPHLLO_MASK 0x0007
+#define TPD_V6_IPHLLO_SHIFT 7
+
+/* tpd word 3 bits 8:9 bit */
+#define TPD_VL_TAGGED_MASK 0x0001
+#define TPD_VL_TAGGED_SHIFT 8
+#define TPD_ETHTYPE_MASK 0x0001
+#define TPD_ETHTYPE_SHIFT 9
+
+/* tdp word 3 bits 10:13 if ip version is 0 */
+#define TDP_V4_IPHL_MASK 0x000F
+#define TPD_V4_IPHL_SHIFT 10
+
+/* tdp word 3 bits 10:13 if ip version is 1 */
+#define TPD_V6_IPHLHI_MASK 0x000F
+#define TPD_V6_IPHLHI_SHIFT 10
+
+/* tpd word 3 bit 14:31 if segment enabled */
+#define TPD_TCPHDRLEN_MASK 0x000F
+#define TPD_TCPHDRLEN_SHIFT 14
+#define TPD_HDRFLAG_MASK 0x0001
+#define TPD_HDRFLAG_SHIFT 18
+#define TPD_MSS_MASK 0x1FFF
+#define TPD_MSS_SHIFT 19
+
+/* tdp word 3 bit 16:31 if custom csum enabled */
+#define TPD_PLOADOFFSET_MASK 0x00FF
+#define TPD_PLOADOFFSET_SHIFT 16
+#define TPD_CCSUMOFFSET_MASK 0x00FF
+#define TPD_CCSUMOFFSET_SHIFT 24
+
+struct atl1e_tpd_desc {
+ __le64 buffer_addr;
+ __le32 word2;
+ __le32 word3;
+};
+
+/* how about 0x2000 */
+#define MAX_TX_BUF_LEN 0x2000
+#define MAX_TX_BUF_SHIFT 13
+/*#define MAX_TX_BUF_LEN 0x3000 */
+
+/* rrs word 1 bit 0:31 */
+#define RRS_RX_CSUM_MASK 0xFFFF
+#define RRS_RX_CSUM_SHIFT 0
+#define RRS_PKT_SIZE_MASK 0x3FFF
+#define RRS_PKT_SIZE_SHIFT 16
+#define RRS_CPU_NUM_MASK 0x0003
+#define RRS_CPU_NUM_SHIFT 30
+
+#define RRS_IS_RSS_IPV4 0x0001
+#define RRS_IS_RSS_IPV4_TCP 0x0002
+#define RRS_IS_RSS_IPV6 0x0004
+#define RRS_IS_RSS_IPV6_TCP 0x0008
+#define RRS_IS_IPV6 0x0010
+#define RRS_IS_IP_FRAG 0x0020
+#define RRS_IS_IP_DF 0x0040
+#define RRS_IS_802_3 0x0080
+#define RRS_IS_VLAN_TAG 0x0100
+#define RRS_IS_ERR_FRAME 0x0200
+#define RRS_IS_IPV4 0x0400
+#define RRS_IS_UDP 0x0800
+#define RRS_IS_TCP 0x1000
+#define RRS_IS_BCAST 0x2000
+#define RRS_IS_MCAST 0x4000
+#define RRS_IS_PAUSE 0x8000
+
+#define RRS_ERR_BAD_CRC 0x0001
+#define RRS_ERR_CODE 0x0002
+#define RRS_ERR_DRIBBLE 0x0004
+#define RRS_ERR_RUNT 0x0008
+#define RRS_ERR_RX_OVERFLOW 0x0010
+#define RRS_ERR_TRUNC 0x0020
+#define RRS_ERR_IP_CSUM 0x0040
+#define RRS_ERR_L4_CSUM 0x0080
+#define RRS_ERR_LENGTH 0x0100
+#define RRS_ERR_DES_ADDR 0x0200
+
+struct atl1e_recv_ret_status {
+ u16 seq_num;
+ u16 hash_lo;
+ __le32 word1;
+ u16 pkt_flag;
+ u16 err_flag;
+ u16 hash_hi;
+ u16 vtag;
+};
+
+enum atl1e_dma_req_block {
+ atl1e_dma_req_128 = 0,
+ atl1e_dma_req_256 = 1,
+ atl1e_dma_req_512 = 2,
+ atl1e_dma_req_1024 = 3,
+ atl1e_dma_req_2048 = 4,
+ atl1e_dma_req_4096 = 5
+};
+
+enum atl1e_rrs_type {
+ atl1e_rrs_disable = 0,
+ atl1e_rrs_ipv4 = 1,
+ atl1e_rrs_ipv4_tcp = 2,
+ atl1e_rrs_ipv6 = 4,
+ atl1e_rrs_ipv6_tcp = 8
+};
+
+enum atl1e_nic_type {
+ athr_l1e = 0,
+ athr_l2e_revA = 1,
+ athr_l2e_revB = 2
+};
+
+struct atl1e_hw_stats {
+ /* rx */
+ unsigned long rx_ok; /* The number of good packet received. */
+ unsigned long rx_bcast; /* The number of good broadcast packet received. */
+ unsigned long rx_mcast; /* The number of good multicast packet received. */
+ unsigned long rx_pause; /* The number of Pause packet received. */
+ unsigned long rx_ctrl; /* The number of Control packet received other than Pause frame. */
+ unsigned long rx_fcs_err; /* The number of packets with bad FCS. */
+ unsigned long rx_len_err; /* The number of packets with mismatch of length field and actual size. */
+ unsigned long rx_byte_cnt; /* The number of bytes of good packet received. FCS is NOT included. */
+ unsigned long rx_runt; /* The number of packets received that are less than 64 byte long and with good FCS. */
+ unsigned long rx_frag; /* The number of packets received that are less than 64 byte long and with bad FCS. */
+ unsigned long rx_sz_64; /* The number of good and bad packets received that are 64 byte long. */
+ unsigned long rx_sz_65_127; /* The number of good and bad packets received that are between 65 and 127-byte long. */
+ unsigned long rx_sz_128_255; /* The number of good and bad packets received that are between 128 and 255-byte long. */
+ unsigned long rx_sz_256_511; /* The number of good and bad packets received that are between 256 and 511-byte long. */
+ unsigned long rx_sz_512_1023; /* The number of good and bad packets received that are between 512 and 1023-byte long. */
+ unsigned long rx_sz_1024_1518; /* The number of good and bad packets received that are between 1024 and 1518-byte long. */
+ unsigned long rx_sz_1519_max; /* The number of good and bad packets received that are between 1519-byte and MTU. */
+ unsigned long rx_sz_ov; /* The number of good and bad packets received that are more than MTU size truncated by Selene. */
+ unsigned long rx_rxf_ov; /* The number of frame dropped due to occurrence of RX FIFO overflow. */
+ unsigned long rx_rrd_ov; /* The number of frame dropped due to occurrence of RRD overflow. */
+ unsigned long rx_align_err; /* Alignment Error */
+ unsigned long rx_bcast_byte_cnt; /* The byte count of broadcast packet received, excluding FCS. */
+ unsigned long rx_mcast_byte_cnt; /* The byte count of multicast packet received, excluding FCS. */
+ unsigned long rx_err_addr; /* The number of packets dropped due to address filtering. */
+
+ /* tx */
+ unsigned long tx_ok; /* The number of good packet transmitted. */
+ unsigned long tx_bcast; /* The number of good broadcast packet transmitted. */
+ unsigned long tx_mcast; /* The number of good multicast packet transmitted. */
+ unsigned long tx_pause; /* The number of Pause packet transmitted. */
+ unsigned long tx_exc_defer; /* The number of packets transmitted with excessive deferral. */
+ unsigned long tx_ctrl; /* The number of packets transmitted is a control frame, excluding Pause frame. */
+ unsigned long tx_defer; /* The number of packets transmitted that is deferred. */
+ unsigned long tx_byte_cnt; /* The number of bytes of data transmitted. FCS is NOT included. */
+ unsigned long tx_sz_64; /* The number of good and bad packets transmitted that are 64 byte long. */
+ unsigned long tx_sz_65_127; /* The number of good and bad packets transmitted that are between 65 and 127-byte long. */
+ unsigned long tx_sz_128_255; /* The number of good and bad packets transmitted that are between 128 and 255-byte long. */
+ unsigned long tx_sz_256_511; /* The number of good and bad packets transmitted that are between 256 and 511-byte long. */
+ unsigned long tx_sz_512_1023; /* The number of good and bad packets transmitted that are between 512 and 1023-byte long. */
+ unsigned long tx_sz_1024_1518; /* The number of good and bad packets transmitted that are between 1024 and 1518-byte long. */
+ unsigned long tx_sz_1519_max; /* The number of good and bad packets transmitted that are between 1519-byte and MTU. */
+ unsigned long tx_1_col; /* The number of packets subsequently transmitted successfully with a single prior collision. */
+ unsigned long tx_2_col; /* The number of packets subsequently transmitted successfully with multiple prior collisions. */
+ unsigned long tx_late_col; /* The number of packets transmitted with late collisions. */
+ unsigned long tx_abort_col; /* The number of transmit packets aborted due to excessive collisions. */
+ unsigned long tx_underrun; /* The number of transmit packets aborted due to transmit FIFO underrun, or TRD FIFO underrun */
+ unsigned long tx_rd_eop; /* The number of times that read beyond the EOP into the next frame area when TRD was not written timely */
+ unsigned long tx_len_err; /* The number of transmit packets with length field does NOT match the actual frame size. */
+ unsigned long tx_trunc; /* The number of transmit packets truncated due to size exceeding MTU. */
+ unsigned long tx_bcast_byte; /* The byte count of broadcast packet transmitted, excluding FCS. */
+ unsigned long tx_mcast_byte; /* The byte count of multicast packet transmitted, excluding FCS. */
+};
+
+struct atl1e_hw {
+ u8 __iomem *hw_addr; /* inner register address */
+ resource_size_t mem_rang;
+ struct atl1e_adapter *adapter;
+ enum atl1e_nic_type nic_type;
+ u16 device_id;
+ u16 vendor_id;
+ u16 subsystem_id;
+ u16 subsystem_vendor_id;
+ u8 revision_id;
+ u16 pci_cmd_word;
+ u8 mac_addr[ETH_ALEN];
+ u8 perm_mac_addr[ETH_ALEN];
+ u8 preamble_len;
+ u16 max_frame_size;
+ u16 rx_jumbo_th;
+ u16 tx_jumbo_th;
+
+ u16 media_type;
+#define MEDIA_TYPE_AUTO_SENSOR 0
+#define MEDIA_TYPE_100M_FULL 1
+#define MEDIA_TYPE_100M_HALF 2
+#define MEDIA_TYPE_10M_FULL 3
+#define MEDIA_TYPE_10M_HALF 4
+
+ u16 autoneg_advertised;
+#define ADVERTISE_10_HALF 0x0001
+#define ADVERTISE_10_FULL 0x0002
+#define ADVERTISE_100_HALF 0x0004
+#define ADVERTISE_100_FULL 0x0008
+#define ADVERTISE_1000_HALF 0x0010 /* Not used, just FYI */
+#define ADVERTISE_1000_FULL 0x0020
+ u16 mii_autoneg_adv_reg;
+ u16 mii_1000t_ctrl_reg;
+
+ u16 imt; /* Interrupt Moderator timer ( 2us resolution) */
+ u16 ict; /* Interrupt Clear timer (2us resolution) */
+ u32 smb_timer;
+ u16 rrd_thresh; /* Threshold of number of RRD produced to trigger
+ interrupt request */
+ u16 tpd_thresh;
+ u16 rx_count_down; /* 2us resolution */
+ u16 tx_count_down;
+
+ u8 tpd_burst; /* Number of TPD to prefetch in cache-aligned burst. */
+ enum atl1e_rrs_type rrs_type;
+ u32 base_cpu;
+ u32 indirect_tab;
+
+ enum atl1e_dma_req_block dmar_block;
+ enum atl1e_dma_req_block dmaw_block;
+ u8 dmaw_dly_cnt;
+ u8 dmar_dly_cnt;
+
+ bool phy_configured;
+ bool re_autoneg;
+ bool emi_ca;
+};
+
+/*
+ * wrapper around a pointer to a socket buffer,
+ * so a DMA handle can be stored along with the buffer
+ */
+struct atl1e_tx_buffer {
+ struct sk_buff *skb;
+ u16 flags;
+#define ATL1E_TX_PCIMAP_SINGLE 0x0001
+#define ATL1E_TX_PCIMAP_PAGE 0x0002
+#define ATL1E_TX_PCIMAP_TYPE_MASK 0x0003
+ u16 length;
+ dma_addr_t dma;
+};
+
+#define ATL1E_SET_PCIMAP_TYPE(tx_buff, type) do { \
+ ((tx_buff)->flags) &= ~ATL1E_TX_PCIMAP_TYPE_MASK; \
+ ((tx_buff)->flags) |= (type); \
+ } while (0)
+
+struct atl1e_rx_page {
+ dma_addr_t dma; /* receive rage DMA address */
+ u8 *addr; /* receive rage virtual address */
+ dma_addr_t write_offset_dma; /* the DMA address which contain the
+ receive data offset in the page */
+ u32 *write_offset_addr; /* the virtaul address which contain
+ the receive data offset in the page */
+ u32 read_offset; /* the offset where we have read */
+};
+
+struct atl1e_rx_page_desc {
+ struct atl1e_rx_page rx_page[AT_PAGE_NUM_PER_QUEUE];
+ u8 rx_using;
+ u16 rx_nxseq;
+};
+
+/* transmit packet descriptor (tpd) ring */
+struct atl1e_tx_ring {
+ struct atl1e_tpd_desc *desc; /* descriptor ring virtual address */
+ dma_addr_t dma; /* descriptor ring physical address */
+ u16 count; /* the count of transmit rings */
+ rwlock_t tx_lock;
+ u16 next_to_use;
+ atomic_t next_to_clean;
+ struct atl1e_tx_buffer *tx_buffer;
+ dma_addr_t cmb_dma;
+ u32 *cmb;
+};
+
+/* receive packet descriptor ring */
+struct atl1e_rx_ring {
+ void *desc;
+ dma_addr_t dma;
+ int size;
+ u32 page_size; /* bytes length of rxf page */
+ u32 real_page_size; /* real_page_size = page_size + jumbo + aliagn */
+ struct atl1e_rx_page_desc rx_page_desc[AT_MAX_RECEIVE_QUEUE];
+};
+
+/* board specific private data structure */
+struct atl1e_adapter {
+ struct net_device *netdev;
+ struct pci_dev *pdev;
+ struct napi_struct napi;
+ struct mii_if_info mii; /* MII interface info */
+ struct atl1e_hw hw;
+ struct atl1e_hw_stats hw_stats;
+
+ bool have_msi;
+ u32 wol;
+ u16 link_speed;
+ u16 link_duplex;
+
+ spinlock_t mdio_lock;
+ spinlock_t tx_lock;
+ atomic_t irq_sem;
+
+ struct work_struct reset_task;
+ struct work_struct link_chg_task;
+ struct timer_list watchdog_timer;
+ struct timer_list phy_config_timer;
+
+ /* All Descriptor memory */
+ dma_addr_t ring_dma;
+ void *ring_vir_addr;
+ u32 ring_size;
+
+ struct atl1e_tx_ring tx_ring;
+ struct atl1e_rx_ring rx_ring;
+ int num_rx_queues;
+ unsigned long flags;
+#define __AT_TESTING 0x0001
+#define __AT_RESETTING 0x0002
+#define __AT_DOWN 0x0003
+
+ u32 bd_number; /* board number;*/
+ u32 pci_state[16];
+ u32 *config_space;
+};
+
+#define AT_WRITE_REG(a, reg, value) ( \
+ writel((value), ((a)->hw_addr + reg)))
+
+#define AT_WRITE_FLUSH(a) (\
+ readl((a)->hw_addr))
+
+#define AT_READ_REG(a, reg) ( \
+ readl((a)->hw_addr + reg))
+
+#define AT_WRITE_REGB(a, reg, value) (\
+ writeb((value), ((a)->hw_addr + reg)))
+
+#define AT_READ_REGB(a, reg) (\
+ readb((a)->hw_addr + reg))
+
+#define AT_WRITE_REGW(a, reg, value) (\
+ writew((value), ((a)->hw_addr + reg)))
+
+#define AT_READ_REGW(a, reg) (\
+ readw((a)->hw_addr + reg))
+
+#define AT_WRITE_REG_ARRAY(a, reg, offset, value) ( \
+ writel((value), (((a)->hw_addr + reg) + ((offset) << 2))))
+
+#define AT_READ_REG_ARRAY(a, reg, offset) ( \
+ readl(((a)->hw_addr + reg) + ((offset) << 2)))
+
+extern char atl1e_driver_name[];
+extern char atl1e_driver_version[];
+
+extern void atl1e_check_options(struct atl1e_adapter *adapter);
+extern int atl1e_up(struct atl1e_adapter *adapter);
+extern void atl1e_down(struct atl1e_adapter *adapter);
+extern void atl1e_reinit_locked(struct atl1e_adapter *adapter);
+extern s32 atl1e_reset_hw(struct atl1e_hw *hw);
+extern void atl1e_set_ethtool_ops(struct net_device *netdev);
+#endif /* _ATL1_E_H_ */
diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c
new file mode 100644
index 0000000..2278d63
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c
@@ -0,0 +1,395 @@
+/*
+ * Copyright(c) 2007 Atheros Corporation. All rights reserved.
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/slab.h>
+
+#include "atl1e.h"
+
+static int atl1e_get_settings(struct net_device *netdev,
+ struct ethtool_cmd *ecmd)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ struct atl1e_hw *hw = &adapter->hw;
+
+ ecmd->supported = (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_Autoneg |
+ SUPPORTED_TP);
+ if (hw->nic_type == athr_l1e)
+ ecmd->supported |= SUPPORTED_1000baseT_Full;
+
+ ecmd->advertising = ADVERTISED_TP;
+
+ ecmd->advertising |= ADVERTISED_Autoneg;
+ ecmd->advertising |= hw->autoneg_advertised;
+
+ ecmd->port = PORT_TP;
+ ecmd->phy_address = 0;
+ ecmd->transceiver = XCVR_INTERNAL;
+
+ if (adapter->link_speed != SPEED_0) {
+ ethtool_cmd_speed_set(ecmd, adapter->link_speed);
+ if (adapter->link_duplex == FULL_DUPLEX)
+ ecmd->duplex = DUPLEX_FULL;
+ else
+ ecmd->duplex = DUPLEX_HALF;
+ } else {
+ ethtool_cmd_speed_set(ecmd, -1);
+ ecmd->duplex = -1;
+ }
+
+ ecmd->autoneg = AUTONEG_ENABLE;
+ return 0;
+}
+
+static int atl1e_set_settings(struct net_device *netdev,
+ struct ethtool_cmd *ecmd)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ struct atl1e_hw *hw = &adapter->hw;
+
+ while (test_and_set_bit(__AT_RESETTING, &adapter->flags))
+ msleep(1);
+
+ if (ecmd->autoneg == AUTONEG_ENABLE) {
+ u16 adv4, adv9;
+
+ if ((ecmd->advertising&ADVERTISE_1000_FULL)) {
+ if (hw->nic_type == athr_l1e) {
+ hw->autoneg_advertised =
+ ecmd->advertising & AT_ADV_MASK;
+ } else {
+ clear_bit(__AT_RESETTING, &adapter->flags);
+ return -EINVAL;
+ }
+ } else if (ecmd->advertising&ADVERTISE_1000_HALF) {
+ clear_bit(__AT_RESETTING, &adapter->flags);
+ return -EINVAL;
+ } else {
+ hw->autoneg_advertised =
+ ecmd->advertising & AT_ADV_MASK;
+ }
+ ecmd->advertising = hw->autoneg_advertised |
+ ADVERTISED_TP | ADVERTISED_Autoneg;
+
+ adv4 = hw->mii_autoneg_adv_reg & ~ADVERTISE_ALL;
+ adv9 = hw->mii_1000t_ctrl_reg & ~MII_AT001_CR_1000T_SPEED_MASK;
+ if (hw->autoneg_advertised & ADVERTISE_10_HALF)
+ adv4 |= ADVERTISE_10HALF;
+ if (hw->autoneg_advertised & ADVERTISE_10_FULL)
+ adv4 |= ADVERTISE_10FULL;
+ if (hw->autoneg_advertised & ADVERTISE_100_HALF)
+ adv4 |= ADVERTISE_100HALF;
+ if (hw->autoneg_advertised & ADVERTISE_100_FULL)
+ adv4 |= ADVERTISE_100FULL;
+ if (hw->autoneg_advertised & ADVERTISE_1000_FULL)
+ adv9 |= ADVERTISE_1000FULL;
+
+ if (adv4 != hw->mii_autoneg_adv_reg ||
+ adv9 != hw->mii_1000t_ctrl_reg) {
+ hw->mii_autoneg_adv_reg = adv4;
+ hw->mii_1000t_ctrl_reg = adv9;
+ hw->re_autoneg = true;
+ }
+
+ } else {
+ clear_bit(__AT_RESETTING, &adapter->flags);
+ return -EINVAL;
+ }
+
+ /* reset the link */
+
+ if (netif_running(adapter->netdev)) {
+ atl1e_down(adapter);
+ atl1e_up(adapter);
+ } else
+ atl1e_reset_hw(&adapter->hw);
+
+ clear_bit(__AT_RESETTING, &adapter->flags);
+ return 0;
+}
+
+static u32 atl1e_get_msglevel(struct net_device *netdev)
+{
+#ifdef DBG
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+static int atl1e_get_regs_len(struct net_device *netdev)
+{
+ return AT_REGS_LEN * sizeof(u32);
+}
+
+static void atl1e_get_regs(struct net_device *netdev,
+ struct ethtool_regs *regs, void *p)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ struct atl1e_hw *hw = &adapter->hw;
+ u32 *regs_buff = p;
+ u16 phy_data;
+
+ memset(p, 0, AT_REGS_LEN * sizeof(u32));
+
+ regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id;
+
+ regs_buff[0] = AT_READ_REG(hw, REG_VPD_CAP);
+ regs_buff[1] = AT_READ_REG(hw, REG_SPI_FLASH_CTRL);
+ regs_buff[2] = AT_READ_REG(hw, REG_SPI_FLASH_CONFIG);
+ regs_buff[3] = AT_READ_REG(hw, REG_TWSI_CTRL);
+ regs_buff[4] = AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL);
+ regs_buff[5] = AT_READ_REG(hw, REG_MASTER_CTRL);
+ regs_buff[6] = AT_READ_REG(hw, REG_MANUAL_TIMER_INIT);
+ regs_buff[7] = AT_READ_REG(hw, REG_IRQ_MODU_TIMER_INIT);
+ regs_buff[8] = AT_READ_REG(hw, REG_GPHY_CTRL);
+ regs_buff[9] = AT_READ_REG(hw, REG_CMBDISDMA_TIMER);
+ regs_buff[10] = AT_READ_REG(hw, REG_IDLE_STATUS);
+ regs_buff[11] = AT_READ_REG(hw, REG_MDIO_CTRL);
+ regs_buff[12] = AT_READ_REG(hw, REG_SERDES_LOCK);
+ regs_buff[13] = AT_READ_REG(hw, REG_MAC_CTRL);
+ regs_buff[14] = AT_READ_REG(hw, REG_MAC_IPG_IFG);
+ regs_buff[15] = AT_READ_REG(hw, REG_MAC_STA_ADDR);
+ regs_buff[16] = AT_READ_REG(hw, REG_MAC_STA_ADDR+4);
+ regs_buff[17] = AT_READ_REG(hw, REG_RX_HASH_TABLE);
+ regs_buff[18] = AT_READ_REG(hw, REG_RX_HASH_TABLE+4);
+ regs_buff[19] = AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL);
+ regs_buff[20] = AT_READ_REG(hw, REG_MTU);
+ regs_buff[21] = AT_READ_REG(hw, REG_WOL_CTRL);
+ regs_buff[22] = AT_READ_REG(hw, REG_SRAM_TRD_ADDR);
+ regs_buff[23] = AT_READ_REG(hw, REG_SRAM_TRD_LEN);
+ regs_buff[24] = AT_READ_REG(hw, REG_SRAM_RXF_ADDR);
+ regs_buff[25] = AT_READ_REG(hw, REG_SRAM_RXF_LEN);
+ regs_buff[26] = AT_READ_REG(hw, REG_SRAM_TXF_ADDR);
+ regs_buff[27] = AT_READ_REG(hw, REG_SRAM_TXF_LEN);
+ regs_buff[28] = AT_READ_REG(hw, REG_SRAM_TCPH_ADDR);
+ regs_buff[29] = AT_READ_REG(hw, REG_SRAM_PKTH_ADDR);
+
+ atl1e_read_phy_reg(hw, MII_BMCR, &phy_data);
+ regs_buff[73] = (u32)phy_data;
+ atl1e_read_phy_reg(hw, MII_BMSR, &phy_data);
+ regs_buff[74] = (u32)phy_data;
+}
+
+static int atl1e_get_eeprom_len(struct net_device *netdev)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+
+ if (!atl1e_check_eeprom_exist(&adapter->hw))
+ return AT_EEPROM_LEN;
+ else
+ return 0;
+}
+
+static int atl1e_get_eeprom(struct net_device *netdev,
+ struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ struct atl1e_hw *hw = &adapter->hw;
+ u32 *eeprom_buff;
+ int first_dword, last_dword;
+ int ret_val = 0;
+ int i;
+
+ if (eeprom->len == 0)
+ return -EINVAL;
+
+ if (atl1e_check_eeprom_exist(hw)) /* not exist */
+ return -EINVAL;
+
+ eeprom->magic = hw->vendor_id | (hw->device_id << 16);
+
+ first_dword = eeprom->offset >> 2;
+ last_dword = (eeprom->offset + eeprom->len - 1) >> 2;
+
+ eeprom_buff = kmalloc(sizeof(u32) *
+ (last_dword - first_dword + 1), GFP_KERNEL);
+ if (eeprom_buff == NULL)
+ return -ENOMEM;
+
+ for (i = first_dword; i < last_dword; i++) {
+ if (!atl1e_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) {
+ kfree(eeprom_buff);
+ return -EIO;
+ }
+ }
+
+ memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3),
+ eeprom->len);
+ kfree(eeprom_buff);
+
+ return ret_val;
+}
+
+static int atl1e_set_eeprom(struct net_device *netdev,
+ struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ struct atl1e_hw *hw = &adapter->hw;
+ u32 *eeprom_buff;
+ u32 *ptr;
+ int first_dword, last_dword;
+ int ret_val = 0;
+ int i;
+
+ if (eeprom->len == 0)
+ return -EOPNOTSUPP;
+
+ if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16)))
+ return -EINVAL;
+
+ first_dword = eeprom->offset >> 2;
+ last_dword = (eeprom->offset + eeprom->len - 1) >> 2;
+ eeprom_buff = kmalloc(AT_EEPROM_LEN, GFP_KERNEL);
+ if (eeprom_buff == NULL)
+ return -ENOMEM;
+
+ ptr = (u32 *)eeprom_buff;
+
+ if (eeprom->offset & 3) {
+ /* need read/modify/write of first changed EEPROM word */
+ /* only the second byte of the word is being modified */
+ if (!atl1e_read_eeprom(hw, first_dword * 4, &(eeprom_buff[0]))) {
+ ret_val = -EIO;
+ goto out;
+ }
+ ptr++;
+ }
+ if (((eeprom->offset + eeprom->len) & 3)) {
+ /* need read/modify/write of last changed EEPROM word */
+ /* only the first byte of the word is being modified */
+
+ if (!atl1e_read_eeprom(hw, last_dword * 4,
+ &(eeprom_buff[last_dword - first_dword]))) {
+ ret_val = -EIO;
+ goto out;
+ }
+ }
+
+ /* Device's eeprom is always little-endian, word addressable */
+ memcpy(ptr, bytes, eeprom->len);
+
+ for (i = 0; i < last_dword - first_dword + 1; i++) {
+ if (!atl1e_write_eeprom(hw, ((first_dword + i) * 4),
+ eeprom_buff[i])) {
+ ret_val = -EIO;
+ goto out;
+ }
+ }
+out:
+ kfree(eeprom_buff);
+ return ret_val;
+}
+
+static void atl1e_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+
+ strncpy(drvinfo->driver, atl1e_driver_name, 32);
+ strncpy(drvinfo->version, atl1e_driver_version, 32);
+ strncpy(drvinfo->fw_version, "L1e", 32);
+ strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);
+ drvinfo->n_stats = 0;
+ drvinfo->testinfo_len = 0;
+ drvinfo->regdump_len = atl1e_get_regs_len(netdev);
+ drvinfo->eedump_len = atl1e_get_eeprom_len(netdev);
+}
+
+static void atl1e_get_wol(struct net_device *netdev,
+ struct ethtool_wolinfo *wol)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+
+ wol->supported = WAKE_MAGIC | WAKE_PHY;
+ wol->wolopts = 0;
+
+ if (adapter->wol & AT_WUFC_EX)
+ wol->wolopts |= WAKE_UCAST;
+ if (adapter->wol & AT_WUFC_MC)
+ wol->wolopts |= WAKE_MCAST;
+ if (adapter->wol & AT_WUFC_BC)
+ wol->wolopts |= WAKE_BCAST;
+ if (adapter->wol & AT_WUFC_MAG)
+ wol->wolopts |= WAKE_MAGIC;
+ if (adapter->wol & AT_WUFC_LNKC)
+ wol->wolopts |= WAKE_PHY;
+}
+
+static int atl1e_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+
+ if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE |
+ WAKE_UCAST | WAKE_MCAST | WAKE_BCAST))
+ return -EOPNOTSUPP;
+ /* these settings will always override what we currently have */
+ adapter->wol = 0;
+
+ if (wol->wolopts & WAKE_MAGIC)
+ adapter->wol |= AT_WUFC_MAG;
+ if (wol->wolopts & WAKE_PHY)
+ adapter->wol |= AT_WUFC_LNKC;
+
+ device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
+
+ return 0;
+}
+
+static int atl1e_nway_reset(struct net_device *netdev)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ if (netif_running(netdev))
+ atl1e_reinit_locked(adapter);
+ return 0;
+}
+
+static const struct ethtool_ops atl1e_ethtool_ops = {
+ .get_settings = atl1e_get_settings,
+ .set_settings = atl1e_set_settings,
+ .get_drvinfo = atl1e_get_drvinfo,
+ .get_regs_len = atl1e_get_regs_len,
+ .get_regs = atl1e_get_regs,
+ .get_wol = atl1e_get_wol,
+ .set_wol = atl1e_set_wol,
+ .get_msglevel = atl1e_get_msglevel,
+ .nway_reset = atl1e_nway_reset,
+ .get_link = ethtool_op_get_link,
+ .get_eeprom_len = atl1e_get_eeprom_len,
+ .get_eeprom = atl1e_get_eeprom,
+ .set_eeprom = atl1e_set_eeprom,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39))
+ .set_tx_csum = ethtool_op_set_tx_hw_csum,
+ .set_sg = ethtool_op_set_sg,
+ .set_tso = ethtool_op_set_tso,
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */
+};
+
+void atl1e_set_ethtool_ops(struct net_device *netdev)
+{
+ SET_ETHTOOL_OPS(netdev, &atl1e_ethtool_ops);
+}
diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_hw.c b/drivers/net/ethernet/atheros/atl1e/atl1e_hw.c
new file mode 100644
index 0000000..923063d
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_hw.c
@@ -0,0 +1,651 @@
+/*
+ * Copyright(c) 2007 Atheros Corporation. All rights reserved.
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ */
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mii.h>
+#include <linux/crc32.h>
+
+#include "atl1e.h"
+
+/*
+ * check_eeprom_exist
+ * return 0 if eeprom exist
+ */
+int atl1e_check_eeprom_exist(struct atl1e_hw *hw)
+{
+ u32 value;
+
+ value = AT_READ_REG(hw, REG_SPI_FLASH_CTRL);
+ if (value & SPI_FLASH_CTRL_EN_VPD) {
+ value &= ~SPI_FLASH_CTRL_EN_VPD;
+ AT_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value);
+ }
+ value = AT_READ_REGW(hw, REG_PCIE_CAP_LIST);
+ return ((value & 0xFF00) == 0x6C00) ? 0 : 1;
+}
+
+void atl1e_hw_set_mac_addr(struct atl1e_hw *hw)
+{
+ u32 value;
+ /*
+ * 00-0B-6A-F6-00-DC
+ * 0: 6AF600DC 1: 000B
+ * low dword
+ */
+ value = (((u32)hw->mac_addr[2]) << 24) |
+ (((u32)hw->mac_addr[3]) << 16) |
+ (((u32)hw->mac_addr[4]) << 8) |
+ (((u32)hw->mac_addr[5])) ;
+ AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value);
+ /* hight dword */
+ value = (((u32)hw->mac_addr[0]) << 8) |
+ (((u32)hw->mac_addr[1])) ;
+ AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value);
+}
+
+/*
+ * atl1e_get_permanent_address
+ * return 0 if get valid mac address,
+ */
+static int atl1e_get_permanent_address(struct atl1e_hw *hw)
+{
+ u32 addr[2];
+ u32 i;
+ u32 twsi_ctrl_data;
+ u8 eth_addr[ETH_ALEN];
+
+ if (is_valid_ether_addr(hw->perm_mac_addr))
+ return 0;
+
+ /* init */
+ addr[0] = addr[1] = 0;
+
+ if (!atl1e_check_eeprom_exist(hw)) {
+ /* eeprom exist */
+ twsi_ctrl_data = AT_READ_REG(hw, REG_TWSI_CTRL);
+ twsi_ctrl_data |= TWSI_CTRL_SW_LDSTART;
+ AT_WRITE_REG(hw, REG_TWSI_CTRL, twsi_ctrl_data);
+ for (i = 0; i < AT_TWSI_EEPROM_TIMEOUT; i++) {
+ msleep(10);
+ twsi_ctrl_data = AT_READ_REG(hw, REG_TWSI_CTRL);
+ if ((twsi_ctrl_data & TWSI_CTRL_SW_LDSTART) == 0)
+ break;
+ }
+ if (i >= AT_TWSI_EEPROM_TIMEOUT)
+ return AT_ERR_TIMEOUT;
+ }
+
+ /* maybe MAC-address is from BIOS */
+ addr[0] = AT_READ_REG(hw, REG_MAC_STA_ADDR);
+ addr[1] = AT_READ_REG(hw, REG_MAC_STA_ADDR + 4);
+ *(u32 *) ð_addr[2] = swab32(addr[0]);
+ *(u16 *) ð_addr[0] = swab16(*(u16 *)&addr[1]);
+
+ if (is_valid_ether_addr(eth_addr)) {
+ memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN);
+ return 0;
+ }
+
+ return AT_ERR_EEPROM;
+}
+
+bool atl1e_write_eeprom(struct atl1e_hw *hw, u32 offset, u32 value)
+{
+ return true;
+}
+
+bool atl1e_read_eeprom(struct atl1e_hw *hw, u32 offset, u32 *p_value)
+{
+ int i;
+ u32 control;
+
+ if (offset & 3)
+ return false; /* address do not align */
+
+ AT_WRITE_REG(hw, REG_VPD_DATA, 0);
+ control = (offset & VPD_CAP_VPD_ADDR_MASK) << VPD_CAP_VPD_ADDR_SHIFT;
+ AT_WRITE_REG(hw, REG_VPD_CAP, control);
+
+ for (i = 0; i < 10; i++) {
+ msleep(2);
+ control = AT_READ_REG(hw, REG_VPD_CAP);
+ if (control & VPD_CAP_VPD_FLAG)
+ break;
+ }
+ if (control & VPD_CAP_VPD_FLAG) {
+ *p_value = AT_READ_REG(hw, REG_VPD_DATA);
+ return true;
+ }
+ return false; /* timeout */
+}
+
+void atl1e_force_ps(struct atl1e_hw *hw)
+{
+ AT_WRITE_REGW(hw, REG_GPHY_CTRL,
+ GPHY_CTRL_PW_WOL_DIS | GPHY_CTRL_EXT_RESET);
+}
+
+/*
+ * Reads the adapter's MAC address from the EEPROM
+ *
+ * hw - Struct containing variables accessed by shared code
+ */
+int atl1e_read_mac_addr(struct atl1e_hw *hw)
+{
+ int err = 0;
+
+ err = atl1e_get_permanent_address(hw);
+ if (err)
+ return AT_ERR_EEPROM;
+ memcpy(hw->mac_addr, hw->perm_mac_addr, sizeof(hw->perm_mac_addr));
+ return 0;
+}
+
+/*
+ * atl1e_hash_mc_addr
+ * purpose
+ * set hash value for a multicast address
+ */
+u32 atl1e_hash_mc_addr(struct atl1e_hw *hw, u8 *mc_addr)
+{
+ u32 crc32;
+ u32 value = 0;
+ int i;
+
+ crc32 = ether_crc_le(6, mc_addr);
+ for (i = 0; i < 32; i++)
+ value |= (((crc32 >> i) & 1) << (31 - i));
+
+ return value;
+}
+
+/*
+ * Sets the bit in the multicast table corresponding to the hash value.
+ * hw - Struct containing variables accessed by shared code
+ * hash_value - Multicast address hash value
+ */
+void atl1e_hash_set(struct atl1e_hw *hw, u32 hash_value)
+{
+ u32 hash_bit, hash_reg;
+ u32 mta;
+
+ /*
+ * The HASH Table is a register array of 2 32-bit registers.
+ * It is treated like an array of 64 bits. We want to set
+ * bit BitArray[hash_value]. So we figure out what register
+ * the bit is in, read it, OR in the new bit, then write
+ * back the new value. The register is determined by the
+ * upper 7 bits of the hash value and the bit within that
+ * register are determined by the lower 5 bits of the value.
+ */
+ hash_reg = (hash_value >> 31) & 0x1;
+ hash_bit = (hash_value >> 26) & 0x1F;
+
+ mta = AT_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg);
+
+ mta |= (1 << hash_bit);
+
+ AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta);
+}
+/*
+ * Reads the value from a PHY register
+ * hw - Struct containing variables accessed by shared code
+ * reg_addr - address of the PHY register to read
+ */
+int atl1e_read_phy_reg(struct atl1e_hw *hw, u16 reg_addr, u16 *phy_data)
+{
+ u32 val;
+ int i;
+
+ val = ((u32)(reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT |
+ MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW |
+ MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT;
+
+ AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
+
+ wmb();
+
+ for (i = 0; i < MDIO_WAIT_TIMES; i++) {
+ udelay(2);
+ val = AT_READ_REG(hw, REG_MDIO_CTRL);
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ break;
+ wmb();
+ }
+ if (!(val & (MDIO_START | MDIO_BUSY))) {
+ *phy_data = (u16)val;
+ return 0;
+ }
+
+ return AT_ERR_PHY;
+}
+
+/*
+ * Writes a value to a PHY register
+ * hw - Struct containing variables accessed by shared code
+ * reg_addr - address of the PHY register to write
+ * data - data to write to the PHY
+ */
+int atl1e_write_phy_reg(struct atl1e_hw *hw, u32 reg_addr, u16 phy_data)
+{
+ int i;
+ u32 val;
+
+ val = ((u32)(phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT |
+ (reg_addr&MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT |
+ MDIO_SUP_PREAMBLE |
+ MDIO_START |
+ MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT;
+
+ AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
+ wmb();
+
+ for (i = 0; i < MDIO_WAIT_TIMES; i++) {
+ udelay(2);
+ val = AT_READ_REG(hw, REG_MDIO_CTRL);
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ break;
+ wmb();
+ }
+
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ return 0;
+
+ return AT_ERR_PHY;
+}
+
+/*
+ * atl1e_init_pcie - init PCIE module
+ */
+static void atl1e_init_pcie(struct atl1e_hw *hw)
+{
+ u32 value;
+ /* comment 2lines below to save more power when sususpend
+ value = LTSSM_TEST_MODE_DEF;
+ AT_WRITE_REG(hw, REG_LTSSM_TEST_MODE, value);
+ */
+
+ /* pcie flow control mode change */
+ value = AT_READ_REG(hw, 0x1008);
+ value |= 0x8000;
+ AT_WRITE_REG(hw, 0x1008, value);
+}
+/*
+ * Configures PHY autoneg and flow control advertisement settings
+ *
+ * hw - Struct containing variables accessed by shared code
+ */
+static int atl1e_phy_setup_autoneg_adv(struct atl1e_hw *hw)
+{
+ s32 ret_val;
+ u16 mii_autoneg_adv_reg;
+ u16 mii_1000t_ctrl_reg;
+
+ if (0 != hw->mii_autoneg_adv_reg)
+ return 0;
+ /* Read the MII Auto-Neg Advertisement Register (Address 4/9). */
+ mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK;
+ mii_1000t_ctrl_reg = MII_AT001_CR_1000T_DEFAULT_CAP_MASK;
+
+ /*
+ * Need to parse autoneg_advertised and set up
+ * the appropriate PHY registers. First we will parse for
+ * autoneg_advertised software override. Since we can advertise
+ * a plethora of combinations, we need to check each bit
+ * individually.
+ */
+
+ /*
+ * First we clear all the 10/100 mb speed bits in the Auto-Neg
+ * Advertisement Register (Address 4) and the 1000 mb speed bits in
+ * the 1000Base-T control Register (Address 9).
+ */
+ mii_autoneg_adv_reg &= ~ADVERTISE_ALL;
+ mii_1000t_ctrl_reg &= ~MII_AT001_CR_1000T_SPEED_MASK;
+
+ /*
+ * Need to parse MediaType and setup the
+ * appropriate PHY registers.
+ */
+ switch (hw->media_type) {
+ case MEDIA_TYPE_AUTO_SENSOR:
+ mii_autoneg_adv_reg |= ADVERTISE_ALL;
+ hw->autoneg_advertised = ADVERTISE_ALL;
+ if (hw->nic_type == athr_l1e) {
+ mii_1000t_ctrl_reg |= ADVERTISE_1000FULL;
+ hw->autoneg_advertised |= ADVERTISE_1000_FULL;
+ }
+ break;
+
+ case MEDIA_TYPE_100M_FULL:
+ mii_autoneg_adv_reg |= ADVERTISE_100FULL;
+ hw->autoneg_advertised = ADVERTISE_100_FULL;
+ break;
+
+ case MEDIA_TYPE_100M_HALF:
+ mii_autoneg_adv_reg |= ADVERTISE_100_HALF;
+ hw->autoneg_advertised = ADVERTISE_100_HALF;
+ break;
+
+ case MEDIA_TYPE_10M_FULL:
+ mii_autoneg_adv_reg |= ADVERTISE_10_FULL;
+ hw->autoneg_advertised = ADVERTISE_10_FULL;
+ break;
+
+ default:
+ mii_autoneg_adv_reg |= ADVERTISE_10_HALF;
+ hw->autoneg_advertised = ADVERTISE_10_HALF;
+ break;
+ }
+
+ /* flow control fixed to enable all */
+ mii_autoneg_adv_reg |= (ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP);
+
+ hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg;
+ hw->mii_1000t_ctrl_reg = mii_1000t_ctrl_reg;
+
+ ret_val = atl1e_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg);
+ if (ret_val)
+ return ret_val;
+
+ if (hw->nic_type == athr_l1e || hw->nic_type == athr_l2e_revA) {
+ ret_val = atl1e_write_phy_reg(hw, MII_CTRL1000,
+ mii_1000t_ctrl_reg);
+ if (ret_val)
+ return ret_val;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Resets the PHY and make all config validate
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Sets bit 15 and 12 of the MII control regiser (for F001 bug)
+ */
+int atl1e_phy_commit(struct atl1e_hw *hw)
+{
+ struct atl1e_adapter *adapter = hw->adapter;
+ int ret_val;
+ u16 phy_data;
+
+ phy_data = BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART;
+
+ ret_val = atl1e_write_phy_reg(hw, MII_BMCR, phy_data);
+ if (ret_val) {
+ u32 val;
+ int i;
+ /**************************************
+ * pcie serdes link may be down !
+ **************************************/
+ for (i = 0; i < 25; i++) {
+ msleep(1);
+ val = AT_READ_REG(hw, REG_MDIO_CTRL);
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ break;
+ }
+
+ if (0 != (val & (MDIO_START | MDIO_BUSY))) {
+ netdev_err(adapter->netdev,
+ "pcie linkdown at least for 25ms\n");
+ return ret_val;
+ }
+
+ netdev_err(adapter->netdev, "pcie linkup after %d ms\n", i);
+ }
+ return 0;
+}
+
+int atl1e_phy_init(struct atl1e_hw *hw)
+{
+ struct atl1e_adapter *adapter = hw->adapter;
+ s32 ret_val;
+ u16 phy_val;
+
+ if (hw->phy_configured) {
+ if (hw->re_autoneg) {
+ hw->re_autoneg = false;
+ return atl1e_restart_autoneg(hw);
+ }
+ return 0;
+ }
+
+ /* RESET GPHY Core */
+ AT_WRITE_REGW(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT);
+ msleep(2);
+ AT_WRITE_REGW(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT |
+ GPHY_CTRL_EXT_RESET);
+ msleep(2);
+
+ /* patches */
+ /* p1. eable hibernation mode */
+ ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0xB);
+ if (ret_val)
+ return ret_val;
+ ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0xBC00);
+ if (ret_val)
+ return ret_val;
+ /* p2. set Class A/B for all modes */
+ ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0);
+ if (ret_val)
+ return ret_val;
+ phy_val = 0x02ef;
+ /* remove Class AB */
+ /* phy_val = hw->emi_ca ? 0x02ef : 0x02df; */
+ ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, phy_val);
+ if (ret_val)
+ return ret_val;
+ /* p3. 10B ??? */
+ ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0x12);
+ if (ret_val)
+ return ret_val;
+ ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0x4C04);
+ if (ret_val)
+ return ret_val;
+ /* p4. 1000T power */
+ ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0x4);
+ if (ret_val)
+ return ret_val;
+ ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0x8BBB);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0x5);
+ if (ret_val)
+ return ret_val;
+ ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0x2C46);
+ if (ret_val)
+ return ret_val;
+
+ msleep(1);
+
+ /*Enable PHY LinkChange Interrupt */
+ ret_val = atl1e_write_phy_reg(hw, MII_INT_CTRL, 0xC00);
+ if (ret_val) {
+ netdev_err(adapter->netdev,
+ "Error enable PHY linkChange Interrupt\n");
+ return ret_val;
+ }
+ /* setup AutoNeg parameters */
+ ret_val = atl1e_phy_setup_autoneg_adv(hw);
+ if (ret_val) {
+ netdev_err(adapter->netdev,
+ "Error Setting up Auto-Negotiation\n");
+ return ret_val;
+ }
+ /* SW.Reset & En-Auto-Neg to restart Auto-Neg*/
+ netdev_dbg(adapter->netdev, "Restarting Auto-Negotiation\n");
+ ret_val = atl1e_phy_commit(hw);
+ if (ret_val) {
+ netdev_err(adapter->netdev, "Error resetting the phy\n");
+ return ret_val;
+ }
+
+ hw->phy_configured = true;
+
+ return 0;
+}
+
+/*
+ * Reset the transmit and receive units; mask and clear all interrupts.
+ * hw - Struct containing variables accessed by shared code
+ * return : 0 or idle status (if error)
+ */
+int atl1e_reset_hw(struct atl1e_hw *hw)
+{
+ struct atl1e_adapter *adapter = hw->adapter;
+ struct pci_dev *pdev = adapter->pdev;
+
+ u32 idle_status_data = 0;
+ u16 pci_cfg_cmd_word = 0;
+ int timeout = 0;
+
+ /* Workaround for PCI problem when BIOS sets MMRBC incorrectly. */
+ pci_read_config_word(pdev, PCI_REG_COMMAND, &pci_cfg_cmd_word);
+ if ((pci_cfg_cmd_word & (CMD_IO_SPACE |
+ CMD_MEMORY_SPACE | CMD_BUS_MASTER))
+ != (CMD_IO_SPACE | CMD_MEMORY_SPACE | CMD_BUS_MASTER)) {
+ pci_cfg_cmd_word |= (CMD_IO_SPACE |
+ CMD_MEMORY_SPACE | CMD_BUS_MASTER);
+ pci_write_config_word(pdev, PCI_REG_COMMAND, pci_cfg_cmd_word);
+ }
+
+ /*
+ * Issue Soft Reset to the MAC. This will reset the chip's
+ * transmit, receive, DMA. It will not effect
+ * the current PCI configuration. The global reset bit is self-
+ * clearing, and should clear within a microsecond.
+ */
+ AT_WRITE_REG(hw, REG_MASTER_CTRL,
+ MASTER_CTRL_LED_MODE | MASTER_CTRL_SOFT_RST);
+ wmb();
+ msleep(1);
+
+ /* Wait at least 10ms for All module to be Idle */
+ for (timeout = 0; timeout < AT_HW_MAX_IDLE_DELAY; timeout++) {
+ idle_status_data = AT_READ_REG(hw, REG_IDLE_STATUS);
+ if (idle_status_data == 0)
+ break;
+ msleep(1);
+ cpu_relax();
+ }
+
+ if (timeout >= AT_HW_MAX_IDLE_DELAY) {
+ netdev_err(adapter->netdev,
+ "MAC state machine can't be idle since disabled for 10ms second\n");
+ return AT_ERR_TIMEOUT;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Performs basic configuration of the adapter.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * Assumes that the controller has previously been reset and is in a
+ * post-reset uninitialized state. Initializes multicast table,
+ * and Calls routines to setup link
+ * Leaves the transmit and receive units disabled and uninitialized.
+ */
+int atl1e_init_hw(struct atl1e_hw *hw)
+{
+ s32 ret_val = 0;
+
+ atl1e_init_pcie(hw);
+
+ /* Zero out the Multicast HASH table */
+ /* clear the old settings from the multicast hash table */
+ AT_WRITE_REG(hw, REG_RX_HASH_TABLE, 0);
+ AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0);
+
+ ret_val = atl1e_phy_init(hw);
+
+ return ret_val;
+}
+
+/*
+ * Detects the current speed and duplex settings of the hardware.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * speed - Speed of the connection
+ * duplex - Duplex setting of the connection
+ */
+int atl1e_get_speed_and_duplex(struct atl1e_hw *hw, u16 *speed, u16 *duplex)
+{
+ int err;
+ u16 phy_data;
+
+ /* Read PHY Specific Status Register (17) */
+ err = atl1e_read_phy_reg(hw, MII_AT001_PSSR, &phy_data);
+ if (err)
+ return err;
+
+ if (!(phy_data & MII_AT001_PSSR_SPD_DPLX_RESOLVED))
+ return AT_ERR_PHY_RES;
+
+ switch (phy_data & MII_AT001_PSSR_SPEED) {
+ case MII_AT001_PSSR_1000MBS:
+ *speed = SPEED_1000;
+ break;
+ case MII_AT001_PSSR_100MBS:
+ *speed = SPEED_100;
+ break;
+ case MII_AT001_PSSR_10MBS:
+ *speed = SPEED_10;
+ break;
+ default:
+ return AT_ERR_PHY_SPEED;
+ break;
+ }
+
+ if (phy_data & MII_AT001_PSSR_DPLX)
+ *duplex = FULL_DUPLEX;
+ else
+ *duplex = HALF_DUPLEX;
+
+ return 0;
+}
+
+int atl1e_restart_autoneg(struct atl1e_hw *hw)
+{
+ int err = 0;
+
+ err = atl1e_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg);
+ if (err)
+ return err;
+
+ if (hw->nic_type == athr_l1e || hw->nic_type == athr_l2e_revA) {
+ err = atl1e_write_phy_reg(hw, MII_CTRL1000,
+ hw->mii_1000t_ctrl_reg);
+ if (err)
+ return err;
+ }
+
+ err = atl1e_write_phy_reg(hw, MII_BMCR,
+ BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART);
+ return err;
+}
+
diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_hw.h b/drivers/net/ethernet/atheros/atl1e/atl1e_hw.h
new file mode 100644
index 0000000..74df16a
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_hw.h
@@ -0,0 +1,690 @@
+/*
+ * Copyright(c) 2007 Atheros Corporation. All rights reserved.
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ */
+
+#ifndef _ATHL1E_HW_H_
+#define _ATHL1E_HW_H_
+
+#include <linux/types.h>
+#include <linux/mii.h>
+
+struct atl1e_adapter;
+struct atl1e_hw;
+
+/* function prototype */
+s32 atl1e_reset_hw(struct atl1e_hw *hw);
+s32 atl1e_read_mac_addr(struct atl1e_hw *hw);
+s32 atl1e_init_hw(struct atl1e_hw *hw);
+s32 atl1e_phy_commit(struct atl1e_hw *hw);
+s32 atl1e_get_speed_and_duplex(struct atl1e_hw *hw, u16 *speed, u16 *duplex);
+u32 atl1e_auto_get_fc(struct atl1e_adapter *adapter, u16 duplex);
+u32 atl1e_hash_mc_addr(struct atl1e_hw *hw, u8 *mc_addr);
+void atl1e_hash_set(struct atl1e_hw *hw, u32 hash_value);
+s32 atl1e_read_phy_reg(struct atl1e_hw *hw, u16 reg_addr, u16 *phy_data);
+s32 atl1e_write_phy_reg(struct atl1e_hw *hw, u32 reg_addr, u16 phy_data);
+s32 atl1e_validate_mdi_setting(struct atl1e_hw *hw);
+void atl1e_hw_set_mac_addr(struct atl1e_hw *hw);
+bool atl1e_read_eeprom(struct atl1e_hw *hw, u32 offset, u32 *p_value);
+bool atl1e_write_eeprom(struct atl1e_hw *hw, u32 offset, u32 value);
+s32 atl1e_phy_enter_power_saving(struct atl1e_hw *hw);
+s32 atl1e_phy_leave_power_saving(struct atl1e_hw *hw);
+s32 atl1e_phy_init(struct atl1e_hw *hw);
+int atl1e_check_eeprom_exist(struct atl1e_hw *hw);
+void atl1e_force_ps(struct atl1e_hw *hw);
+s32 atl1e_restart_autoneg(struct atl1e_hw *hw);
+
+/* register definition */
+#define REG_PM_CTRLSTAT 0x44
+
+#define REG_PCIE_CAP_LIST 0x58
+
+#define REG_DEVICE_CAP 0x5C
+#define DEVICE_CAP_MAX_PAYLOAD_MASK 0x7
+#define DEVICE_CAP_MAX_PAYLOAD_SHIFT 0
+
+#define REG_DEVICE_CTRL 0x60
+#define DEVICE_CTRL_MAX_PAYLOAD_MASK 0x7
+#define DEVICE_CTRL_MAX_PAYLOAD_SHIFT 5
+#define DEVICE_CTRL_MAX_RREQ_SZ_MASK 0x7
+#define DEVICE_CTRL_MAX_RREQ_SZ_SHIFT 12
+
+#define REG_VPD_CAP 0x6C
+#define VPD_CAP_ID_MASK 0xff
+#define VPD_CAP_ID_SHIFT 0
+#define VPD_CAP_NEXT_PTR_MASK 0xFF
+#define VPD_CAP_NEXT_PTR_SHIFT 8
+#define VPD_CAP_VPD_ADDR_MASK 0x7FFF
+#define VPD_CAP_VPD_ADDR_SHIFT 16
+#define VPD_CAP_VPD_FLAG 0x80000000
+
+#define REG_VPD_DATA 0x70
+
+#define REG_SPI_FLASH_CTRL 0x200
+#define SPI_FLASH_CTRL_STS_NON_RDY 0x1
+#define SPI_FLASH_CTRL_STS_WEN 0x2
+#define SPI_FLASH_CTRL_STS_WPEN 0x80
+#define SPI_FLASH_CTRL_DEV_STS_MASK 0xFF
+#define SPI_FLASH_CTRL_DEV_STS_SHIFT 0
+#define SPI_FLASH_CTRL_INS_MASK 0x7
+#define SPI_FLASH_CTRL_INS_SHIFT 8
+#define SPI_FLASH_CTRL_START 0x800
+#define SPI_FLASH_CTRL_EN_VPD 0x2000
+#define SPI_FLASH_CTRL_LDSTART 0x8000
+#define SPI_FLASH_CTRL_CS_HI_MASK 0x3
+#define SPI_FLASH_CTRL_CS_HI_SHIFT 16
+#define SPI_FLASH_CTRL_CS_HOLD_MASK 0x3
+#define SPI_FLASH_CTRL_CS_HOLD_SHIFT 18
+#define SPI_FLASH_CTRL_CLK_LO_MASK 0x3
+#define SPI_FLASH_CTRL_CLK_LO_SHIFT 20
+#define SPI_FLASH_CTRL_CLK_HI_MASK 0x3
+#define SPI_FLASH_CTRL_CLK_HI_SHIFT 22
+#define SPI_FLASH_CTRL_CS_SETUP_MASK 0x3
+#define SPI_FLASH_CTRL_CS_SETUP_SHIFT 24
+#define SPI_FLASH_CTRL_EROM_PGSZ_MASK 0x3
+#define SPI_FLASH_CTRL_EROM_PGSZ_SHIFT 26
+#define SPI_FLASH_CTRL_WAIT_READY 0x10000000
+
+#define REG_SPI_ADDR 0x204
+
+#define REG_SPI_DATA 0x208
+
+#define REG_SPI_FLASH_CONFIG 0x20C
+#define SPI_FLASH_CONFIG_LD_ADDR_MASK 0xFFFFFF
+#define SPI_FLASH_CONFIG_LD_ADDR_SHIFT 0
+#define SPI_FLASH_CONFIG_VPD_ADDR_MASK 0x3
+#define SPI_FLASH_CONFIG_VPD_ADDR_SHIFT 24
+#define SPI_FLASH_CONFIG_LD_EXIST 0x4000000
+
+
+#define REG_SPI_FLASH_OP_PROGRAM 0x210
+#define REG_SPI_FLASH_OP_SC_ERASE 0x211
+#define REG_SPI_FLASH_OP_CHIP_ERASE 0x212
+#define REG_SPI_FLASH_OP_RDID 0x213
+#define REG_SPI_FLASH_OP_WREN 0x214
+#define REG_SPI_FLASH_OP_RDSR 0x215
+#define REG_SPI_FLASH_OP_WRSR 0x216
+#define REG_SPI_FLASH_OP_READ 0x217
+
+#define REG_TWSI_CTRL 0x218
+#define TWSI_CTRL_LD_OFFSET_MASK 0xFF
+#define TWSI_CTRL_LD_OFFSET_SHIFT 0
+#define TWSI_CTRL_LD_SLV_ADDR_MASK 0x7
+#define TWSI_CTRL_LD_SLV_ADDR_SHIFT 8
+#define TWSI_CTRL_SW_LDSTART 0x800
+#define TWSI_CTRL_HW_LDSTART 0x1000
+#define TWSI_CTRL_SMB_SLV_ADDR_MASK 0x0x7F
+#define TWSI_CTRL_SMB_SLV_ADDR_SHIFT 15
+#define TWSI_CTRL_LD_EXIST 0x400000
+#define TWSI_CTRL_READ_FREQ_SEL_MASK 0x3
+#define TWSI_CTRL_READ_FREQ_SEL_SHIFT 23
+#define TWSI_CTRL_FREQ_SEL_100K 0
+#define TWSI_CTRL_FREQ_SEL_200K 1
+#define TWSI_CTRL_FREQ_SEL_300K 2
+#define TWSI_CTRL_FREQ_SEL_400K 3
+#define TWSI_CTRL_SMB_SLV_ADDR
+#define TWSI_CTRL_WRITE_FREQ_SEL_MASK 0x3
+#define TWSI_CTRL_WRITE_FREQ_SEL_SHIFT 24
+
+
+#define REG_PCIE_DEV_MISC_CTRL 0x21C
+#define PCIE_DEV_MISC_CTRL_EXT_PIPE 0x2
+#define PCIE_DEV_MISC_CTRL_RETRY_BUFDIS 0x1
+#define PCIE_DEV_MISC_CTRL_SPIROM_EXIST 0x4
+#define PCIE_DEV_MISC_CTRL_SERDES_ENDIAN 0x8
+#define PCIE_DEV_MISC_CTRL_SERDES_SEL_DIN 0x10
+
+#define REG_PCIE_PHYMISC 0x1000
+#define PCIE_PHYMISC_FORCE_RCV_DET 0x4
+
+#define REG_LTSSM_TEST_MODE 0x12FC
+#define LTSSM_TEST_MODE_DEF 0xE000
+
+/* Selene Master Control Register */
+#define REG_MASTER_CTRL 0x1400
+#define MASTER_CTRL_SOFT_RST 0x1
+#define MASTER_CTRL_MTIMER_EN 0x2
+#define MASTER_CTRL_ITIMER_EN 0x4
+#define MASTER_CTRL_MANUAL_INT 0x8
+#define MASTER_CTRL_ITIMER2_EN 0x20
+#define MASTER_CTRL_INT_RDCLR 0x40
+#define MASTER_CTRL_LED_MODE 0x200
+#define MASTER_CTRL_REV_NUM_SHIFT 16
+#define MASTER_CTRL_REV_NUM_MASK 0xff
+#define MASTER_CTRL_DEV_ID_SHIFT 24
+#define MASTER_CTRL_DEV_ID_MASK 0xff
+
+/* Timer Initial Value Register */
+#define REG_MANUAL_TIMER_INIT 0x1404
+
+
+/* IRQ ModeratorTimer Initial Value Register */
+#define REG_IRQ_MODU_TIMER_INIT 0x1408 /* w */
+#define REG_IRQ_MODU_TIMER2_INIT 0x140A /* w */
+
+
+#define REG_GPHY_CTRL 0x140C
+#define GPHY_CTRL_EXT_RESET 1
+#define GPHY_CTRL_PIPE_MOD 2
+#define GPHY_CTRL_TEST_MODE_MASK 3
+#define GPHY_CTRL_TEST_MODE_SHIFT 2
+#define GPHY_CTRL_BERT_START 0x10
+#define GPHY_CTRL_GATE_25M_EN 0x20
+#define GPHY_CTRL_LPW_EXIT 0x40
+#define GPHY_CTRL_PHY_IDDQ 0x80
+#define GPHY_CTRL_PHY_IDDQ_DIS 0x100
+#define GPHY_CTRL_PCLK_SEL_DIS 0x200
+#define GPHY_CTRL_HIB_EN 0x400
+#define GPHY_CTRL_HIB_PULSE 0x800
+#define GPHY_CTRL_SEL_ANA_RST 0x1000
+#define GPHY_CTRL_PHY_PLL_ON 0x2000
+#define GPHY_CTRL_PWDOWN_HW 0x4000
+#define GPHY_CTRL_DEFAULT (\
+ GPHY_CTRL_PHY_PLL_ON |\
+ GPHY_CTRL_SEL_ANA_RST |\
+ GPHY_CTRL_HIB_PULSE |\
+ GPHY_CTRL_HIB_EN)
+
+#define GPHY_CTRL_PW_WOL_DIS (\
+ GPHY_CTRL_PHY_PLL_ON |\
+ GPHY_CTRL_SEL_ANA_RST |\
+ GPHY_CTRL_HIB_PULSE |\
+ GPHY_CTRL_HIB_EN |\
+ GPHY_CTRL_PWDOWN_HW |\
+ GPHY_CTRL_PCLK_SEL_DIS |\
+ GPHY_CTRL_PHY_IDDQ)
+
+/* IRQ Anti-Lost Timer Initial Value Register */
+#define REG_CMBDISDMA_TIMER 0x140E
+
+
+/* Block IDLE Status Register */
+#define REG_IDLE_STATUS 0x1410
+#define IDLE_STATUS_RXMAC 1 /* 1: RXMAC state machine is in non-IDLE state. 0: RXMAC is idling */
+#define IDLE_STATUS_TXMAC 2 /* 1: TXMAC state machine is in non-IDLE state. 0: TXMAC is idling */
+#define IDLE_STATUS_RXQ 4 /* 1: RXQ state machine is in non-IDLE state. 0: RXQ is idling */
+#define IDLE_STATUS_TXQ 8 /* 1: TXQ state machine is in non-IDLE state. 0: TXQ is idling */
+#define IDLE_STATUS_DMAR 0x10 /* 1: DMAR state machine is in non-IDLE state. 0: DMAR is idling */
+#define IDLE_STATUS_DMAW 0x20 /* 1: DMAW state machine is in non-IDLE state. 0: DMAW is idling */
+#define IDLE_STATUS_SMB 0x40 /* 1: SMB state machine is in non-IDLE state. 0: SMB is idling */
+#define IDLE_STATUS_CMB 0x80 /* 1: CMB state machine is in non-IDLE state. 0: CMB is idling */
+
+/* MDIO Control Register */
+#define REG_MDIO_CTRL 0x1414
+#define MDIO_DATA_MASK 0xffff /* On MDIO write, the 16-bit control data to write to PHY MII management register */
+#define MDIO_DATA_SHIFT 0 /* On MDIO read, the 16-bit status data that was read from the PHY MII management register*/
+#define MDIO_REG_ADDR_MASK 0x1f /* MDIO register address */
+#define MDIO_REG_ADDR_SHIFT 16
+#define MDIO_RW 0x200000 /* 1: read, 0: write */
+#define MDIO_SUP_PREAMBLE 0x400000 /* Suppress preamble */
+#define MDIO_START 0x800000 /* Write 1 to initiate the MDIO master. And this bit is self cleared after one cycle*/
+#define MDIO_CLK_SEL_SHIFT 24
+#define MDIO_CLK_25_4 0
+#define MDIO_CLK_25_6 2
+#define MDIO_CLK_25_8 3
+#define MDIO_CLK_25_10 4
+#define MDIO_CLK_25_14 5
+#define MDIO_CLK_25_20 6
+#define MDIO_CLK_25_28 7
+#define MDIO_BUSY 0x8000000
+#define MDIO_AP_EN 0x10000000
+#define MDIO_WAIT_TIMES 10
+
+/* MII PHY Status Register */
+#define REG_PHY_STATUS 0x1418
+#define PHY_STATUS_100M 0x20000
+#define PHY_STATUS_EMI_CA 0x40000
+
+/* BIST Control and Status Register0 (for the Packet Memory) */
+#define REG_BIST0_CTRL 0x141c
+#define BIST0_NOW 0x1 /* 1: To trigger BIST0 logic. This bit stays high during the */
+/* BIST process and reset to zero when BIST is done */
+#define BIST0_SRAM_FAIL 0x2 /* 1: The SRAM failure is un-repairable because it has address */
+/* decoder failure or more than 1 cell stuck-to-x failure */
+#define BIST0_FUSE_FLAG 0x4 /* 1: Indicating one cell has been fixed */
+
+/* BIST Control and Status Register1(for the retry buffer of PCI Express) */
+#define REG_BIST1_CTRL 0x1420
+#define BIST1_NOW 0x1 /* 1: To trigger BIST0 logic. This bit stays high during the */
+/* BIST process and reset to zero when BIST is done */
+#define BIST1_SRAM_FAIL 0x2 /* 1: The SRAM failure is un-repairable because it has address */
+/* decoder failure or more than 1 cell stuck-to-x failure.*/
+#define BIST1_FUSE_FLAG 0x4
+
+/* SerDes Lock Detect Control and Status Register */
+#define REG_SERDES_LOCK 0x1424
+#define SERDES_LOCK_DETECT 1 /* 1: SerDes lock detected . This signal comes from Analog SerDes */
+#define SERDES_LOCK_DETECT_EN 2 /* 1: Enable SerDes Lock detect function */
+
+/* MAC Control Register */
+#define REG_MAC_CTRL 0x1480
+#define MAC_CTRL_TX_EN 1 /* 1: Transmit Enable */
+#define MAC_CTRL_RX_EN 2 /* 1: Receive Enable */
+#define MAC_CTRL_TX_FLOW 4 /* 1: Transmit Flow Control Enable */
+#define MAC_CTRL_RX_FLOW 8 /* 1: Receive Flow Control Enable */
+#define MAC_CTRL_LOOPBACK 0x10 /* 1: Loop back at G/MII Interface */
+#define MAC_CTRL_DUPLX 0x20 /* 1: Full-duplex mode 0: Half-duplex mode */
+#define MAC_CTRL_ADD_CRC 0x40 /* 1: Instruct MAC to attach CRC on all egress Ethernet frames */
+#define MAC_CTRL_PAD 0x80 /* 1: Instruct MAC to pad short frames to 60-bytes, and then attach CRC. This bit has higher priority over CRC_EN */
+#define MAC_CTRL_LENCHK 0x100 /* 1: Instruct MAC to check if length field matches the real packet length */
+#define MAC_CTRL_HUGE_EN 0x200 /* 1: receive Jumbo frame enable */
+#define MAC_CTRL_PRMLEN_SHIFT 10 /* Preamble length */
+#define MAC_CTRL_PRMLEN_MASK 0xf
+#define MAC_CTRL_RMV_VLAN 0x4000 /* 1: to remove VLAN Tag automatically from all receive packets */
+#define MAC_CTRL_PROMIS_EN 0x8000 /* 1: Promiscuous Mode Enable */
+#define MAC_CTRL_TX_PAUSE 0x10000 /* 1: transmit test pause */
+#define MAC_CTRL_SCNT 0x20000 /* 1: shortcut slot time counter */
+#define MAC_CTRL_SRST_TX 0x40000 /* 1: synchronized reset Transmit MAC module */
+#define MAC_CTRL_TX_SIMURST 0x80000 /* 1: transmit simulation reset */
+#define MAC_CTRL_SPEED_SHIFT 20 /* 10: gigabit 01:10M/100M */
+#define MAC_CTRL_SPEED_MASK 0x300000
+#define MAC_CTRL_SPEED_1000 2
+#define MAC_CTRL_SPEED_10_100 1
+#define MAC_CTRL_DBG_TX_BKPRESURE 0x400000 /* 1: transmit maximum backoff (half-duplex test bit) */
+#define MAC_CTRL_TX_HUGE 0x800000 /* 1: transmit huge enable */
+#define MAC_CTRL_RX_CHKSUM_EN 0x1000000 /* 1: RX checksum enable */
+#define MAC_CTRL_MC_ALL_EN 0x2000000 /* 1: upload all multicast frame without error to system */
+#define MAC_CTRL_BC_EN 0x4000000 /* 1: upload all broadcast frame without error to system */
+#define MAC_CTRL_DBG 0x8000000 /* 1: upload all received frame to system (Debug Mode) */
+
+/* MAC IPG/IFG Control Register */
+#define REG_MAC_IPG_IFG 0x1484
+#define MAC_IPG_IFG_IPGT_SHIFT 0 /* Desired back to back inter-packet gap. The default is 96-bit time */
+#define MAC_IPG_IFG_IPGT_MASK 0x7f
+#define MAC_IPG_IFG_MIFG_SHIFT 8 /* Minimum number of IFG to enforce in between RX frames */
+#define MAC_IPG_IFG_MIFG_MASK 0xff /* Frame gap below such IFP is dropped */
+#define MAC_IPG_IFG_IPGR1_SHIFT 16 /* 64bit Carrier-Sense window */
+#define MAC_IPG_IFG_IPGR1_MASK 0x7f
+#define MAC_IPG_IFG_IPGR2_SHIFT 24 /* 96-bit IPG window */
+#define MAC_IPG_IFG_IPGR2_MASK 0x7f
+
+/* MAC STATION ADDRESS */
+#define REG_MAC_STA_ADDR 0x1488
+
+/* Hash table for multicast address */
+#define REG_RX_HASH_TABLE 0x1490
+
+
+/* MAC Half-Duplex Control Register */
+#define REG_MAC_HALF_DUPLX_CTRL 0x1498
+#define MAC_HALF_DUPLX_CTRL_LCOL_SHIFT 0 /* Collision Window */
+#define MAC_HALF_DUPLX_CTRL_LCOL_MASK 0x3ff
+#define MAC_HALF_DUPLX_CTRL_RETRY_SHIFT 12 /* Retransmission maximum, afterwards the packet will be discarded */
+#define MAC_HALF_DUPLX_CTRL_RETRY_MASK 0xf
+#define MAC_HALF_DUPLX_CTRL_EXC_DEF_EN 0x10000 /* 1: Allow the transmission of a packet which has been excessively deferred */
+#define MAC_HALF_DUPLX_CTRL_NO_BACK_C 0x20000 /* 1: No back-off on collision, immediately start the retransmission */
+#define MAC_HALF_DUPLX_CTRL_NO_BACK_P 0x40000 /* 1: No back-off on backpressure, immediately start the transmission after back pressure */
+#define MAC_HALF_DUPLX_CTRL_ABEBE 0x80000 /* 1: Alternative Binary Exponential Back-off Enabled */
+#define MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT 20 /* Maximum binary exponential number */
+#define MAC_HALF_DUPLX_CTRL_ABEBT_MASK 0xf
+#define MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT 24 /* IPG to start JAM for collision based flow control in half-duplex */
+#define MAC_HALF_DUPLX_CTRL_JAMIPG_MASK 0xf /* mode. In unit of 8-bit time */
+
+/* Maximum Frame Length Control Register */
+#define REG_MTU 0x149c
+
+/* Wake-On-Lan control register */
+#define REG_WOL_CTRL 0x14a0
+#define WOL_PATTERN_EN 0x00000001
+#define WOL_PATTERN_PME_EN 0x00000002
+#define WOL_MAGIC_EN 0x00000004
+#define WOL_MAGIC_PME_EN 0x00000008
+#define WOL_LINK_CHG_EN 0x00000010
+#define WOL_LINK_CHG_PME_EN 0x00000020
+#define WOL_PATTERN_ST 0x00000100
+#define WOL_MAGIC_ST 0x00000200
+#define WOL_LINKCHG_ST 0x00000400
+#define WOL_CLK_SWITCH_EN 0x00008000
+#define WOL_PT0_EN 0x00010000
+#define WOL_PT1_EN 0x00020000
+#define WOL_PT2_EN 0x00040000
+#define WOL_PT3_EN 0x00080000
+#define WOL_PT4_EN 0x00100000
+#define WOL_PT5_EN 0x00200000
+#define WOL_PT6_EN 0x00400000
+/* WOL Length ( 2 DWORD ) */
+#define REG_WOL_PATTERN_LEN 0x14a4
+#define WOL_PT_LEN_MASK 0x7f
+#define WOL_PT0_LEN_SHIFT 0
+#define WOL_PT1_LEN_SHIFT 8
+#define WOL_PT2_LEN_SHIFT 16
+#define WOL_PT3_LEN_SHIFT 24
+#define WOL_PT4_LEN_SHIFT 0
+#define WOL_PT5_LEN_SHIFT 8
+#define WOL_PT6_LEN_SHIFT 16
+
+/* Internal SRAM Partition Register */
+#define REG_SRAM_TRD_ADDR 0x1518
+#define REG_SRAM_TRD_LEN 0x151C
+#define REG_SRAM_RXF_ADDR 0x1520
+#define REG_SRAM_RXF_LEN 0x1524
+#define REG_SRAM_TXF_ADDR 0x1528
+#define REG_SRAM_TXF_LEN 0x152C
+#define REG_SRAM_TCPH_ADDR 0x1530
+#define REG_SRAM_PKTH_ADDR 0x1532
+
+/* Load Ptr Register */
+#define REG_LOAD_PTR 0x1534 /* Software sets this bit after the initialization of the head and tail */
+
+/*
+ * addresses of all descriptors, as well as the following descriptor
+ * control register, which triggers each function block to load the head
+ * pointer to prepare for the operation. This bit is then self-cleared
+ * after one cycle.
+ */
+
+/* Descriptor Control register */
+#define REG_RXF3_BASE_ADDR_HI 0x153C
+#define REG_DESC_BASE_ADDR_HI 0x1540
+#define REG_RXF0_BASE_ADDR_HI 0x1540 /* share with DESC BASE ADDR HI */
+#define REG_HOST_RXF0_PAGE0_LO 0x1544
+#define REG_HOST_RXF0_PAGE1_LO 0x1548
+#define REG_TPD_BASE_ADDR_LO 0x154C
+#define REG_RXF1_BASE_ADDR_HI 0x1550
+#define REG_RXF2_BASE_ADDR_HI 0x1554
+#define REG_HOST_RXFPAGE_SIZE 0x1558
+#define REG_TPD_RING_SIZE 0x155C
+/* RSS about */
+#define REG_RSS_KEY0 0x14B0
+#define REG_RSS_KEY1 0x14B4
+#define REG_RSS_KEY2 0x14B8
+#define REG_RSS_KEY3 0x14BC
+#define REG_RSS_KEY4 0x14C0
+#define REG_RSS_KEY5 0x14C4
+#define REG_RSS_KEY6 0x14C8
+#define REG_RSS_KEY7 0x14CC
+#define REG_RSS_KEY8 0x14D0
+#define REG_RSS_KEY9 0x14D4
+#define REG_IDT_TABLE4 0x14E0
+#define REG_IDT_TABLE5 0x14E4
+#define REG_IDT_TABLE6 0x14E8
+#define REG_IDT_TABLE7 0x14EC
+#define REG_IDT_TABLE0 0x1560
+#define REG_IDT_TABLE1 0x1564
+#define REG_IDT_TABLE2 0x1568
+#define REG_IDT_TABLE3 0x156C
+#define REG_IDT_TABLE REG_IDT_TABLE0
+#define REG_RSS_HASH_VALUE 0x1570
+#define REG_RSS_HASH_FLAG 0x1574
+#define REG_BASE_CPU_NUMBER 0x157C
+
+
+/* TXQ Control Register */
+#define REG_TXQ_CTRL 0x1580
+#define TXQ_CTRL_NUM_TPD_BURST_MASK 0xF
+#define TXQ_CTRL_NUM_TPD_BURST_SHIFT 0
+#define TXQ_CTRL_EN 0x20 /* 1: Enable TXQ */
+#define TXQ_CTRL_ENH_MODE 0x40 /* Performance enhancement mode, in which up to two back-to-back DMA read commands might be dispatched. */
+#define TXQ_CTRL_TXF_BURST_NUM_SHIFT 16 /* Number of data byte to read in a cache-aligned burst. Each SRAM entry is 8-byte in length. */
+#define TXQ_CTRL_TXF_BURST_NUM_MASK 0xffff
+
+/* Jumbo packet Threshold for task offload */
+#define REG_TX_EARLY_TH 0x1584 /* Jumbo frame threshold in QWORD unit. Packet greater than */
+/* JUMBO_TASK_OFFLOAD_THRESHOLD will not be task offloaded. */
+#define TX_TX_EARLY_TH_MASK 0x7ff
+#define TX_TX_EARLY_TH_SHIFT 0
+
+
+/* RXQ Control Register */
+#define REG_RXQ_CTRL 0x15A0
+#define RXQ_CTRL_PBA_ALIGN_32 0 /* rx-packet alignment */
+#define RXQ_CTRL_PBA_ALIGN_64 1
+#define RXQ_CTRL_PBA_ALIGN_128 2
+#define RXQ_CTRL_PBA_ALIGN_256 3
+#define RXQ_CTRL_Q1_EN 0x10
+#define RXQ_CTRL_Q2_EN 0x20
+#define RXQ_CTRL_Q3_EN 0x40
+#define RXQ_CTRL_IPV6_XSUM_VERIFY_EN 0x80
+#define RXQ_CTRL_HASH_TLEN_SHIFT 8
+#define RXQ_CTRL_HASH_TLEN_MASK 0xFF
+#define RXQ_CTRL_HASH_TYPE_IPV4 0x10000
+#define RXQ_CTRL_HASH_TYPE_IPV4_TCP 0x20000
+#define RXQ_CTRL_HASH_TYPE_IPV6 0x40000
+#define RXQ_CTRL_HASH_TYPE_IPV6_TCP 0x80000
+#define RXQ_CTRL_RSS_MODE_DISABLE 0
+#define RXQ_CTRL_RSS_MODE_SQSINT 0x4000000
+#define RXQ_CTRL_RSS_MODE_MQUESINT 0x8000000
+#define RXQ_CTRL_RSS_MODE_MQUEMINT 0xC000000
+#define RXQ_CTRL_NIP_QUEUE_SEL_TBL 0x10000000
+#define RXQ_CTRL_HASH_ENABLE 0x20000000
+#define RXQ_CTRL_CUT_THRU_EN 0x40000000
+#define RXQ_CTRL_EN 0x80000000
+
+/* Rx jumbo packet threshold and rrd retirement timer */
+#define REG_RXQ_JMBOSZ_RRDTIM 0x15A4
+/*
+ * Jumbo packet threshold for non-VLAN packet, in QWORD (64-bit) unit.
+ * When the packet length greater than or equal to this value, RXQ
+ * shall start cut-through forwarding of the received packet.
+ */
+#define RXQ_JMBOSZ_TH_MASK 0x7ff
+#define RXQ_JMBOSZ_TH_SHIFT 0 /* RRD retirement timer. Decrement by 1 after every 512ns passes*/
+#define RXQ_JMBO_LKAH_MASK 0xf
+#define RXQ_JMBO_LKAH_SHIFT 11
+
+/* RXF flow control register */
+#define REG_RXQ_RXF_PAUSE_THRESH 0x15A8
+#define RXQ_RXF_PAUSE_TH_HI_SHIFT 0
+#define RXQ_RXF_PAUSE_TH_HI_MASK 0xfff
+#define RXQ_RXF_PAUSE_TH_LO_SHIFT 16
+#define RXQ_RXF_PAUSE_TH_LO_MASK 0xfff
+
+
+/* DMA Engine Control Register */
+#define REG_DMA_CTRL 0x15C0
+#define DMA_CTRL_DMAR_IN_ORDER 0x1
+#define DMA_CTRL_DMAR_ENH_ORDER 0x2
+#define DMA_CTRL_DMAR_OUT_ORDER 0x4
+#define DMA_CTRL_RCB_VALUE 0x8
+#define DMA_CTRL_DMAR_BURST_LEN_SHIFT 4
+#define DMA_CTRL_DMAR_BURST_LEN_MASK 7
+#define DMA_CTRL_DMAW_BURST_LEN_SHIFT 7
+#define DMA_CTRL_DMAW_BURST_LEN_MASK 7
+#define DMA_CTRL_DMAR_REQ_PRI 0x400
+#define DMA_CTRL_DMAR_DLY_CNT_MASK 0x1F
+#define DMA_CTRL_DMAR_DLY_CNT_SHIFT 11
+#define DMA_CTRL_DMAW_DLY_CNT_MASK 0xF
+#define DMA_CTRL_DMAW_DLY_CNT_SHIFT 16
+#define DMA_CTRL_TXCMB_EN 0x100000
+#define DMA_CTRL_RXCMB_EN 0x200000
+
+
+/* CMB/SMB Control Register */
+#define REG_SMB_STAT_TIMER 0x15C4
+#define REG_TRIG_RRD_THRESH 0x15CA
+#define REG_TRIG_TPD_THRESH 0x15C8
+#define REG_TRIG_TXTIMER 0x15CC
+#define REG_TRIG_RXTIMER 0x15CE
+
+/* HOST RXF Page 1,2,3 address */
+#define REG_HOST_RXF1_PAGE0_LO 0x15D0
+#define REG_HOST_RXF1_PAGE1_LO 0x15D4
+#define REG_HOST_RXF2_PAGE0_LO 0x15D8
+#define REG_HOST_RXF2_PAGE1_LO 0x15DC
+#define REG_HOST_RXF3_PAGE0_LO 0x15E0
+#define REG_HOST_RXF3_PAGE1_LO 0x15E4
+
+/* Mail box */
+#define REG_MB_RXF1_RADDR 0x15B4
+#define REG_MB_RXF2_RADDR 0x15B8
+#define REG_MB_RXF3_RADDR 0x15BC
+#define REG_MB_TPD_PROD_IDX 0x15F0
+
+/* RXF-Page 0-3 PageNo & Valid bit */
+#define REG_HOST_RXF0_PAGE0_VLD 0x15F4
+#define HOST_RXF_VALID 1
+#define HOST_RXF_PAGENO_SHIFT 1
+#define HOST_RXF_PAGENO_MASK 0x7F
+#define REG_HOST_RXF0_PAGE1_VLD 0x15F5
+#define REG_HOST_RXF1_PAGE0_VLD 0x15F6
+#define REG_HOST_RXF1_PAGE1_VLD 0x15F7
+#define REG_HOST_RXF2_PAGE0_VLD 0x15F8
+#define REG_HOST_RXF2_PAGE1_VLD 0x15F9
+#define REG_HOST_RXF3_PAGE0_VLD 0x15FA
+#define REG_HOST_RXF3_PAGE1_VLD 0x15FB
+
+/* Interrupt Status Register */
+#define REG_ISR 0x1600
+#define ISR_SMB 1
+#define ISR_TIMER 2 /* Interrupt when Timer is counted down to zero */
+/*
+ * Software manual interrupt, for debug. Set when SW_MAN_INT_EN is set
+ * in Table 51 Selene Master Control Register (Offset 0x1400).
+ */
+#define ISR_MANUAL 4
+#define ISR_HW_RXF_OV 8 /* RXF overflow interrupt */
+#define ISR_HOST_RXF0_OV 0x10
+#define ISR_HOST_RXF1_OV 0x20
+#define ISR_HOST_RXF2_OV 0x40
+#define ISR_HOST_RXF3_OV 0x80
+#define ISR_TXF_UN 0x100
+#define ISR_RX0_PAGE_FULL 0x200
+#define ISR_DMAR_TO_RST 0x400
+#define ISR_DMAW_TO_RST 0x800
+#define ISR_GPHY 0x1000
+#define ISR_TX_CREDIT 0x2000
+#define ISR_GPHY_LPW 0x4000 /* GPHY low power state interrupt */
+#define ISR_RX_PKT 0x10000 /* One packet received, triggered by RFD */
+#define ISR_TX_PKT 0x20000 /* One packet transmitted, triggered by TPD */
+#define ISR_TX_DMA 0x40000
+#define ISR_RX_PKT_1 0x80000
+#define ISR_RX_PKT_2 0x100000
+#define ISR_RX_PKT_3 0x200000
+#define ISR_MAC_RX 0x400000
+#define ISR_MAC_TX 0x800000
+#define ISR_UR_DETECTED 0x1000000
+#define ISR_FERR_DETECTED 0x2000000
+#define ISR_NFERR_DETECTED 0x4000000
+#define ISR_CERR_DETECTED 0x8000000
+#define ISR_PHY_LINKDOWN 0x10000000
+#define ISR_DIS_INT 0x80000000
+
+
+/* Interrupt Mask Register */
+#define REG_IMR 0x1604
+
+
+#define IMR_NORMAL_MASK (\
+ ISR_SMB |\
+ ISR_TXF_UN |\
+ ISR_HW_RXF_OV |\
+ ISR_HOST_RXF0_OV|\
+ ISR_MANUAL |\
+ ISR_GPHY |\
+ ISR_GPHY_LPW |\
+ ISR_DMAR_TO_RST |\
+ ISR_DMAW_TO_RST |\
+ ISR_PHY_LINKDOWN|\
+ ISR_RX_PKT |\
+ ISR_TX_PKT)
+
+#define ISR_TX_EVENT (ISR_TXF_UN | ISR_TX_PKT)
+#define ISR_RX_EVENT (ISR_HOST_RXF0_OV | ISR_HW_RXF_OV | ISR_RX_PKT)
+
+#define REG_MAC_RX_STATUS_BIN 0x1700
+#define REG_MAC_RX_STATUS_END 0x175c
+#define REG_MAC_TX_STATUS_BIN 0x1760
+#define REG_MAC_TX_STATUS_END 0x17c0
+
+/* Hardware Offset Register */
+#define REG_HOST_RXF0_PAGEOFF 0x1800
+#define REG_TPD_CONS_IDX 0x1804
+#define REG_HOST_RXF1_PAGEOFF 0x1808
+#define REG_HOST_RXF2_PAGEOFF 0x180C
+#define REG_HOST_RXF3_PAGEOFF 0x1810
+
+/* RXF-Page 0-3 Offset DMA Address */
+#define REG_HOST_RXF0_MB0_LO 0x1820
+#define REG_HOST_RXF0_MB1_LO 0x1824
+#define REG_HOST_RXF1_MB0_LO 0x1828
+#define REG_HOST_RXF1_MB1_LO 0x182C
+#define REG_HOST_RXF2_MB0_LO 0x1830
+#define REG_HOST_RXF2_MB1_LO 0x1834
+#define REG_HOST_RXF3_MB0_LO 0x1838
+#define REG_HOST_RXF3_MB1_LO 0x183C
+
+/* Tpd CMB DMA Address */
+#define REG_HOST_TX_CMB_LO 0x1840
+#define REG_HOST_SMB_ADDR_LO 0x1844
+
+/* DEBUG ADDR */
+#define REG_DEBUG_DATA0 0x1900
+#define REG_DEBUG_DATA1 0x1904
+
+/***************************** MII definition ***************************************/
+/* PHY Common Register */
+#define MII_AT001_PSCR 0x10
+#define MII_AT001_PSSR 0x11
+#define MII_INT_CTRL 0x12
+#define MII_INT_STATUS 0x13
+#define MII_SMARTSPEED 0x14
+#define MII_LBRERROR 0x18
+#define MII_RESV2 0x1a
+
+#define MII_DBG_ADDR 0x1D
+#define MII_DBG_DATA 0x1E
+
+/* Autoneg Advertisement Register */
+#define MII_AR_DEFAULT_CAP_MASK 0
+
+/* 1000BASE-T Control Register */
+#define MII_AT001_CR_1000T_SPEED_MASK \
+ (ADVERTISE_1000FULL | ADVERTISE_1000HALF)
+#define MII_AT001_CR_1000T_DEFAULT_CAP_MASK MII_AT001_CR_1000T_SPEED_MASK
+
+/* AT001 PHY Specific Control Register */
+#define MII_AT001_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */
+#define MII_AT001_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */
+#define MII_AT001_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */
+#define MII_AT001_PSCR_MAC_POWERDOWN 0x0008
+#define MII_AT001_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low,
+ * 0=CLK125 toggling
+ */
+#define MII_AT001_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5 */
+/* Manual MDI configuration */
+#define MII_AT001_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */
+#define MII_AT001_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover,
+ * 100BASE-TX/10BASE-T:
+ * MDI Mode
+ */
+#define MII_AT001_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled
+ * all speeds.
+ */
+#define MII_AT001_PSCR_10BT_EXT_DIST_ENABLE 0x0080
+/* 1=Enable Extended 10BASE-T distance
+ * (Lower 10BASE-T RX Threshold)
+ * 0=Normal 10BASE-T RX Threshold */
+#define MII_AT001_PSCR_MII_5BIT_ENABLE 0x0100
+/* 1=5-Bit interface in 100BASE-TX
+ * 0=MII interface in 100BASE-TX */
+#define MII_AT001_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler disable */
+#define MII_AT001_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */
+#define MII_AT001_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */
+#define MII_AT001_PSCR_POLARITY_REVERSAL_SHIFT 1
+#define MII_AT001_PSCR_AUTO_X_MODE_SHIFT 5
+#define MII_AT001_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7
+/* AT001 PHY Specific Status Register */
+#define MII_AT001_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */
+#define MII_AT001_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */
+#define MII_AT001_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */
+#define MII_AT001_PSSR_10MBS 0x0000 /* 00=10Mbs */
+#define MII_AT001_PSSR_100MBS 0x4000 /* 01=100Mbs */
+#define MII_AT001_PSSR_1000MBS 0x8000 /* 10=1000Mbs */
+
+#endif /*_ATHL1E_HW_H_*/
diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
new file mode 100644
index 0000000..8a6f738
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
@@ -0,0 +1,2573 @@
+/*
+ * Copyright(c) 2007 Atheros Corporation. All rights reserved.
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ */
+
+#include "atl1e.h"
+
+#define DRV_VERSION "1.0.0.7-NAPI"
+
+char atl1e_driver_name[] = "ATL1E";
+char atl1e_driver_version[] = DRV_VERSION;
+#define PCI_DEVICE_ID_ATTANSIC_L1E 0x1026
+/*
+ * atl1e_pci_tbl - PCI Device ID Table
+ *
+ * Wildcard entries (PCI_ANY_ID) should come last
+ * Last entry must be all 0s
+ *
+ * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
+ * Class, Class Mask, private data (not used) }
+ */
+static DEFINE_PCI_DEVICE_TABLE(atl1e_pci_tbl) = {
+ {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L1E)},
+ {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, 0x1066)},
+ /* required last entry */
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, atl1e_pci_tbl);
+
+MODULE_AUTHOR("Atheros Corporation, <xiong.huang@atheros.com>, Jie Yang <jie.yang@atheros.com>");
+MODULE_DESCRIPTION("Atheros 1000M Ethernet Network Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+static void atl1e_setup_mac_ctrl(struct atl1e_adapter *adapter);
+
+static const u16
+atl1e_rx_page_vld_regs[AT_MAX_RECEIVE_QUEUE][AT_PAGE_NUM_PER_QUEUE] =
+{
+ {REG_HOST_RXF0_PAGE0_VLD, REG_HOST_RXF0_PAGE1_VLD},
+ {REG_HOST_RXF1_PAGE0_VLD, REG_HOST_RXF1_PAGE1_VLD},
+ {REG_HOST_RXF2_PAGE0_VLD, REG_HOST_RXF2_PAGE1_VLD},
+ {REG_HOST_RXF3_PAGE0_VLD, REG_HOST_RXF3_PAGE1_VLD}
+};
+
+static const u16 atl1e_rx_page_hi_addr_regs[AT_MAX_RECEIVE_QUEUE] =
+{
+ REG_RXF0_BASE_ADDR_HI,
+ REG_RXF1_BASE_ADDR_HI,
+ REG_RXF2_BASE_ADDR_HI,
+ REG_RXF3_BASE_ADDR_HI
+};
+
+static const u16
+atl1e_rx_page_lo_addr_regs[AT_MAX_RECEIVE_QUEUE][AT_PAGE_NUM_PER_QUEUE] =
+{
+ {REG_HOST_RXF0_PAGE0_LO, REG_HOST_RXF0_PAGE1_LO},
+ {REG_HOST_RXF1_PAGE0_LO, REG_HOST_RXF1_PAGE1_LO},
+ {REG_HOST_RXF2_PAGE0_LO, REG_HOST_RXF2_PAGE1_LO},
+ {REG_HOST_RXF3_PAGE0_LO, REG_HOST_RXF3_PAGE1_LO}
+};
+
+static const u16
+atl1e_rx_page_write_offset_regs[AT_MAX_RECEIVE_QUEUE][AT_PAGE_NUM_PER_QUEUE] =
+{
+ {REG_HOST_RXF0_MB0_LO, REG_HOST_RXF0_MB1_LO},
+ {REG_HOST_RXF1_MB0_LO, REG_HOST_RXF1_MB1_LO},
+ {REG_HOST_RXF2_MB0_LO, REG_HOST_RXF2_MB1_LO},
+ {REG_HOST_RXF3_MB0_LO, REG_HOST_RXF3_MB1_LO}
+};
+
+static const u16 atl1e_pay_load_size[] = {
+ 128, 256, 512, 1024, 2048, 4096,
+};
+
+/*
+ * atl1e_irq_enable - Enable default interrupt generation settings
+ * @adapter: board private structure
+ */
+static inline void atl1e_irq_enable(struct atl1e_adapter *adapter)
+{
+ if (likely(atomic_dec_and_test(&adapter->irq_sem))) {
+ AT_WRITE_REG(&adapter->hw, REG_ISR, 0);
+ AT_WRITE_REG(&adapter->hw, REG_IMR, IMR_NORMAL_MASK);
+ AT_WRITE_FLUSH(&adapter->hw);
+ }
+}
+
+/*
+ * atl1e_irq_disable - Mask off interrupt generation on the NIC
+ * @adapter: board private structure
+ */
+static inline void atl1e_irq_disable(struct atl1e_adapter *adapter)
+{
+ atomic_inc(&adapter->irq_sem);
+ AT_WRITE_REG(&adapter->hw, REG_IMR, 0);
+ AT_WRITE_FLUSH(&adapter->hw);
+ synchronize_irq(adapter->pdev->irq);
+}
+
+/*
+ * atl1e_irq_reset - reset interrupt confiure on the NIC
+ * @adapter: board private structure
+ */
+static inline void atl1e_irq_reset(struct atl1e_adapter *adapter)
+{
+ atomic_set(&adapter->irq_sem, 0);
+ AT_WRITE_REG(&adapter->hw, REG_ISR, 0);
+ AT_WRITE_REG(&adapter->hw, REG_IMR, 0);
+ AT_WRITE_FLUSH(&adapter->hw);
+}
+
+/*
+ * atl1e_phy_config - Timer Call-back
+ * @data: pointer to netdev cast into an unsigned long
+ */
+static void atl1e_phy_config(unsigned long data)
+{
+ struct atl1e_adapter *adapter = (struct atl1e_adapter *) data;
+ struct atl1e_hw *hw = &adapter->hw;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->mdio_lock, flags);
+ atl1e_restart_autoneg(hw);
+ spin_unlock_irqrestore(&adapter->mdio_lock, flags);
+}
+
+void atl1e_reinit_locked(struct atl1e_adapter *adapter)
+{
+
+ WARN_ON(in_interrupt());
+ while (test_and_set_bit(__AT_RESETTING, &adapter->flags))
+ msleep(1);
+ atl1e_down(adapter);
+ atl1e_up(adapter);
+ clear_bit(__AT_RESETTING, &adapter->flags);
+}
+
+static void atl1e_reset_task(struct work_struct *work)
+{
+ struct atl1e_adapter *adapter;
+ adapter = container_of(work, struct atl1e_adapter, reset_task);
+
+ atl1e_reinit_locked(adapter);
+}
+
+static int atl1e_check_link(struct atl1e_adapter *adapter)
+{
+ struct atl1e_hw *hw = &adapter->hw;
+ struct net_device *netdev = adapter->netdev;
+ int err = 0;
+ u16 speed, duplex, phy_data;
+
+ /* MII_BMSR must read twice */
+ atl1e_read_phy_reg(hw, MII_BMSR, &phy_data);
+ atl1e_read_phy_reg(hw, MII_BMSR, &phy_data);
+ if ((phy_data & BMSR_LSTATUS) == 0) {
+ /* link down */
+ if (netif_carrier_ok(netdev)) { /* old link state: Up */
+ u32 value;
+ /* disable rx */
+ value = AT_READ_REG(hw, REG_MAC_CTRL);
+ value &= ~MAC_CTRL_RX_EN;
+ AT_WRITE_REG(hw, REG_MAC_CTRL, value);
+ adapter->link_speed = SPEED_0;
+ netif_carrier_off(netdev);
+ netif_stop_queue(netdev);
+ }
+ } else {
+ /* Link Up */
+ err = atl1e_get_speed_and_duplex(hw, &speed, &duplex);
+ if (unlikely(err))
+ return err;
+
+ /* link result is our setting */
+ if (adapter->link_speed != speed ||
+ adapter->link_duplex != duplex) {
+ adapter->link_speed = speed;
+ adapter->link_duplex = duplex;
+ atl1e_setup_mac_ctrl(adapter);
+ netdev_info(netdev,
+ "NIC Link is Up <%d Mbps %s Duplex>\n",
+ adapter->link_speed,
+ adapter->link_duplex == FULL_DUPLEX ?
+ "Full" : "Half");
+ }
+
+ if (!netif_carrier_ok(netdev)) {
+ /* Link down -> Up */
+ netif_carrier_on(netdev);
+ netif_wake_queue(netdev);
+ }
+ }
+ return 0;
+}
+
+/*
+ * atl1e_link_chg_task - deal with link change event Out of interrupt context
+ * @netdev: network interface device structure
+ */
+static void atl1e_link_chg_task(struct work_struct *work)
+{
+ struct atl1e_adapter *adapter;
+ unsigned long flags;
+
+ adapter = container_of(work, struct atl1e_adapter, link_chg_task);
+ spin_lock_irqsave(&adapter->mdio_lock, flags);
+ atl1e_check_link(adapter);
+ spin_unlock_irqrestore(&adapter->mdio_lock, flags);
+}
+
+static void atl1e_link_chg_event(struct atl1e_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ u16 phy_data = 0;
+ u16 link_up = 0;
+
+ spin_lock(&adapter->mdio_lock);
+ atl1e_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
+ atl1e_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
+ spin_unlock(&adapter->mdio_lock);
+ link_up = phy_data & BMSR_LSTATUS;
+ /* notify upper layer link down ASAP */
+ if (!link_up) {
+ if (netif_carrier_ok(netdev)) {
+ /* old link state: Up */
+ netdev_info(netdev, "NIC Link is Down\n");
+ adapter->link_speed = SPEED_0;
+ netif_stop_queue(netdev);
+ }
+ }
+ schedule_work(&adapter->link_chg_task);
+}
+
+static void atl1e_del_timer(struct atl1e_adapter *adapter)
+{
+ del_timer_sync(&adapter->phy_config_timer);
+}
+
+static void atl1e_cancel_work(struct atl1e_adapter *adapter)
+{
+ cancel_work_sync(&adapter->reset_task);
+ cancel_work_sync(&adapter->link_chg_task);
+}
+
+/*
+ * atl1e_tx_timeout - Respond to a Tx Hang
+ * @netdev: network interface device structure
+ */
+static void atl1e_tx_timeout(struct net_device *netdev)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+
+ /* Do the reset outside of interrupt context */
+ schedule_work(&adapter->reset_task);
+}
+
+/*
+ * atl1e_set_multi - Multicast and Promiscuous mode set
+ * @netdev: network interface device structure
+ *
+ * The set_multi entry point is called whenever the multicast address
+ * list or the network interface flags are updated. This routine is
+ * responsible for configuring the hardware for proper multicast,
+ * promiscuous mode, and all-multi behavior.
+ */
+static void atl1e_set_multi(struct net_device *netdev)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ struct atl1e_hw *hw = &adapter->hw;
+ struct netdev_hw_addr *ha;
+ u32 mac_ctrl_data = 0;
+ u32 hash_value;
+
+ /* Check for Promiscuous and All Multicast modes */
+ mac_ctrl_data = AT_READ_REG(hw, REG_MAC_CTRL);
+
+ if (netdev->flags & IFF_PROMISC) {
+ mac_ctrl_data |= MAC_CTRL_PROMIS_EN;
+ } else if (netdev->flags & IFF_ALLMULTI) {
+ mac_ctrl_data |= MAC_CTRL_MC_ALL_EN;
+ mac_ctrl_data &= ~MAC_CTRL_PROMIS_EN;
+ } else {
+ mac_ctrl_data &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN);
+ }
+
+ AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data);
+
+ /* clear the old settings from the multicast hash table */
+ AT_WRITE_REG(hw, REG_RX_HASH_TABLE, 0);
+ AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0);
+
+ /* comoute mc addresses' hash value ,and put it into hash table */
+ netdev_for_each_mc_addr(ha, netdev) {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ hash_value = atl1e_hash_mc_addr(hw, ha->addr);
+#else
+ hash_value = atl1e_hash_mc_addr(hw, ha->dmi_addr);
+#endif
+ atl1e_hash_set(hw, hash_value);
+ }
+}
+
+static void __atl1e_vlan_mode(u32 features, u32 *mac_ctrl_data)
+{
+ if (features & NETIF_F_HW_VLAN_RX) {
+ /* enable VLAN tag insert/strip */
+ *mac_ctrl_data |= MAC_CTRL_RMV_VLAN;
+ } else {
+ /* disable VLAN tag insert/strip */
+ *mac_ctrl_data &= ~MAC_CTRL_RMV_VLAN;
+ }
+}
+
+static void atl1e_vlan_mode(struct net_device *netdev, u32 features)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ u32 mac_ctrl_data = 0;
+
+ netdev_dbg(adapter->netdev, "%s\n", __func__);
+
+ atl1e_irq_disable(adapter);
+ mac_ctrl_data = AT_READ_REG(&adapter->hw, REG_MAC_CTRL);
+ __atl1e_vlan_mode(features, &mac_ctrl_data);
+ AT_WRITE_REG(&adapter->hw, REG_MAC_CTRL, mac_ctrl_data);
+ atl1e_irq_enable(adapter);
+}
+
+static void atl1e_restore_vlan(struct atl1e_adapter *adapter)
+{
+ netdev_dbg(adapter->netdev, "%s\n", __func__);
+ atl1e_vlan_mode(adapter->netdev, adapter->netdev->features);
+}
+
+/*
+ * atl1e_set_mac - Change the Ethernet Address of the NIC
+ * @netdev: network interface device structure
+ * @p: pointer to an address structure
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int atl1e_set_mac_addr(struct net_device *netdev, void *p)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ struct sockaddr *addr = p;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ if (netif_running(netdev))
+ return -EBUSY;
+
+ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+ memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len);
+
+ atl1e_hw_set_mac_addr(&adapter->hw);
+
+ return 0;
+}
+
+static u32 atl1e_fix_features(struct net_device *netdev, u32 features)
+{
+ /*
+ * Since there is no support for separate rx/tx vlan accel
+ * enable/disable make sure tx flag is always in same state as rx.
+ */
+ if (features & NETIF_F_HW_VLAN_RX)
+ features |= NETIF_F_HW_VLAN_TX;
+ else
+ features &= ~NETIF_F_HW_VLAN_TX;
+
+ return features;
+}
+
+static int atl1e_set_features(struct net_device *netdev, u32 features)
+{
+ u32 changed = netdev->features ^ features;
+
+ if (changed & NETIF_F_HW_VLAN_RX)
+ atl1e_vlan_mode(netdev, features);
+
+ return 0;
+}
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */
+
+/*
+ * atl1e_change_mtu - Change the Maximum Transfer Unit
+ * @netdev: network interface device structure
+ * @new_mtu: new value for maximum frame size
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int atl1e_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ int old_mtu = netdev->mtu;
+ int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+
+ if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) ||
+ (max_frame > MAX_JUMBO_FRAME_SIZE)) {
+ netdev_warn(adapter->netdev, "invalid MTU setting\n");
+ return -EINVAL;
+ }
+ /* set MTU */
+ if (old_mtu != new_mtu && netif_running(netdev)) {
+ while (test_and_set_bit(__AT_RESETTING, &adapter->flags))
+ msleep(1);
+ netdev->mtu = new_mtu;
+ adapter->hw.max_frame_size = new_mtu;
+ adapter->hw.rx_jumbo_th = (max_frame + 7) >> 3;
+ atl1e_down(adapter);
+ atl1e_up(adapter);
+ clear_bit(__AT_RESETTING, &adapter->flags);
+ }
+ return 0;
+}
+
+/*
+ * caller should hold mdio_lock
+ */
+static int atl1e_mdio_read(struct net_device *netdev, int phy_id, int reg_num)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ u16 result;
+
+ atl1e_read_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, &result);
+ return result;
+}
+
+static void atl1e_mdio_write(struct net_device *netdev, int phy_id,
+ int reg_num, int val)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+
+ atl1e_write_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, val);
+}
+
+/*
+ * atl1e_mii_ioctl -
+ * @netdev:
+ * @ifreq:
+ * @cmd:
+ */
+static int atl1e_mii_ioctl(struct net_device *netdev,
+ struct ifreq *ifr, int cmd)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ struct mii_ioctl_data *data = if_mii(ifr);
+ unsigned long flags;
+ int retval = 0;
+
+ if (!netif_running(netdev))
+ return -EINVAL;
+
+ spin_lock_irqsave(&adapter->mdio_lock, flags);
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ data->phy_id = 0;
+ break;
+
+ case SIOCGMIIREG:
+ if (atl1e_read_phy_reg(&adapter->hw, data->reg_num & 0x1F,
+ &data->val_out)) {
+ retval = -EIO;
+ goto out;
+ }
+ break;
+
+ case SIOCSMIIREG:
+ if (data->reg_num & ~(0x1F)) {
+ retval = -EFAULT;
+ goto out;
+ }
+
+ netdev_dbg(adapter->netdev, "<atl1e_mii_ioctl> write %x %x\n",
+ data->reg_num, data->val_in);
+ if (atl1e_write_phy_reg(&adapter->hw,
+ data->reg_num, data->val_in)) {
+ retval = -EIO;
+ goto out;
+ }
+ break;
+
+ default:
+ retval = -EOPNOTSUPP;
+ break;
+ }
+out:
+ spin_unlock_irqrestore(&adapter->mdio_lock, flags);
+ return retval;
+
+}
+
+/*
+ * atl1e_ioctl -
+ * @netdev:
+ * @ifreq:
+ * @cmd:
+ */
+static int atl1e_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ case SIOCGMIIREG:
+ case SIOCSMIIREG:
+ return atl1e_mii_ioctl(netdev, ifr, cmd);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void atl1e_setup_pcicmd(struct pci_dev *pdev)
+{
+ u16 cmd;
+
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ cmd &= ~(PCI_COMMAND_INTX_DISABLE | PCI_COMMAND_IO);
+ cmd |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
+
+ /*
+ * some motherboards BIOS(PXE/EFI) driver may set PME
+ * while they transfer control to OS (Windows/Linux)
+ * so we should clear this bit before NIC work normally
+ */
+ pci_write_config_dword(pdev, REG_PM_CTRLSTAT, 0);
+ msleep(1);
+}
+
+/*
+ * atl1e_alloc_queues - Allocate memory for all rings
+ * @adapter: board private structure to initialize
+ *
+ */
+static int __devinit atl1e_alloc_queues(struct atl1e_adapter *adapter)
+{
+ return 0;
+}
+
+/*
+ * atl1e_sw_init - Initialize general software structures (struct atl1e_adapter)
+ * @adapter: board private structure to initialize
+ *
+ * atl1e_sw_init initializes the Adapter private data structure.
+ * Fields are initialized based on PCI device information and
+ * OS network device settings (MTU size).
+ */
+static int __devinit atl1e_sw_init(struct atl1e_adapter *adapter)
+{
+ struct atl1e_hw *hw = &adapter->hw;
+ struct pci_dev *pdev = adapter->pdev;
+ u32 phy_status_data = 0;
+
+ adapter->wol = 0;
+ adapter->link_speed = SPEED_0; /* hardware init */
+ adapter->link_duplex = FULL_DUPLEX;
+ adapter->num_rx_queues = 1;
+
+ /* PCI config space info */
+ hw->vendor_id = pdev->vendor;
+ hw->device_id = pdev->device;
+ hw->subsystem_vendor_id = pdev->subsystem_vendor;
+ hw->subsystem_id = pdev->subsystem_device;
+ hw->revision_id = pdev->revision;
+
+ pci_read_config_word(pdev, PCI_COMMAND, &hw->pci_cmd_word);
+
+ phy_status_data = AT_READ_REG(hw, REG_PHY_STATUS);
+ /* nic type */
+ if (hw->revision_id >= 0xF0) {
+ hw->nic_type = athr_l2e_revB;
+ } else {
+ if (phy_status_data & PHY_STATUS_100M)
+ hw->nic_type = athr_l1e;
+ else
+ hw->nic_type = athr_l2e_revA;
+ }
+
+ phy_status_data = AT_READ_REG(hw, REG_PHY_STATUS);
+
+ if (phy_status_data & PHY_STATUS_EMI_CA)
+ hw->emi_ca = true;
+ else
+ hw->emi_ca = false;
+
+ hw->phy_configured = false;
+ hw->preamble_len = 7;
+ hw->max_frame_size = adapter->netdev->mtu;
+ hw->rx_jumbo_th = (hw->max_frame_size + ETH_HLEN +
+ VLAN_HLEN + ETH_FCS_LEN + 7) >> 3;
+
+ hw->rrs_type = atl1e_rrs_disable;
+ hw->indirect_tab = 0;
+ hw->base_cpu = 0;
+
+ /* need confirm */
+
+ hw->ict = 50000; /* 100ms */
+ hw->smb_timer = 200000; /* 200ms */
+ hw->tpd_burst = 5;
+ hw->rrd_thresh = 1;
+ hw->tpd_thresh = adapter->tx_ring.count / 2;
+ hw->rx_count_down = 4; /* 2us resolution */
+ hw->tx_count_down = hw->imt * 4 / 3;
+ hw->dmar_block = atl1e_dma_req_1024;
+ hw->dmaw_block = atl1e_dma_req_1024;
+ hw->dmar_dly_cnt = 15;
+ hw->dmaw_dly_cnt = 4;
+
+ if (atl1e_alloc_queues(adapter)) {
+ netdev_err(adapter->netdev, "Unable to allocate memory for queues\n");
+ return -ENOMEM;
+ }
+
+ atomic_set(&adapter->irq_sem, 1);
+ spin_lock_init(&adapter->mdio_lock);
+ spin_lock_init(&adapter->tx_lock);
+
+ set_bit(__AT_DOWN, &adapter->flags);
+
+ return 0;
+}
+
+/*
+ * atl1e_clean_tx_ring - Free Tx-skb
+ * @adapter: board private structure
+ */
+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_buffer *tx_buffer = NULL;
+ struct pci_dev *pdev = adapter->pdev;
+ u16 index, ring_count;
+
+ if (tx_ring->desc == NULL || tx_ring->tx_buffer == NULL)
+ return;
+
+ ring_count = tx_ring->count;
+ /* first unmmap dma */
+ for (index = 0; index < ring_count; index++) {
+ tx_buffer = &tx_ring->tx_buffer[index];
+ if (tx_buffer->dma) {
+ if (tx_buffer->flags & ATL1E_TX_PCIMAP_SINGLE)
+ pci_unmap_single(pdev, tx_buffer->dma,
+ tx_buffer->length, PCI_DMA_TODEVICE);
+ else if (tx_buffer->flags & ATL1E_TX_PCIMAP_PAGE)
+ pci_unmap_page(pdev, tx_buffer->dma,
+ tx_buffer->length, PCI_DMA_TODEVICE);
+ tx_buffer->dma = 0;
+ }
+ }
+ /* second free skb */
+ for (index = 0; index < ring_count; index++) {
+ tx_buffer = &tx_ring->tx_buffer[index];
+ if (tx_buffer->skb) {
+ dev_kfree_skb_any(tx_buffer->skb);
+ tx_buffer->skb = NULL;
+ }
+ }
+ /* Zero out Tx-buffers */
+ memset(tx_ring->desc, 0, sizeof(struct atl1e_tpd_desc) *
+ ring_count);
+ memset(tx_ring->tx_buffer, 0, sizeof(struct atl1e_tx_buffer) *
+ ring_count);
+}
+
+/*
+ * atl1e_clean_rx_ring - Free rx-reservation skbs
+ * @adapter: board private structure
+ */
+static void atl1e_clean_rx_ring(struct atl1e_adapter *adapter)
+{
+ struct atl1e_rx_ring *rx_ring =
+ (struct atl1e_rx_ring *)&adapter->rx_ring;
+ struct atl1e_rx_page_desc *rx_page_desc = rx_ring->rx_page_desc;
+ u16 i, j;
+
+
+ if (adapter->ring_vir_addr == NULL)
+ return;
+ /* Zero out the descriptor ring */
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) {
+ if (rx_page_desc[i].rx_page[j].addr != NULL) {
+ memset(rx_page_desc[i].rx_page[j].addr, 0,
+ rx_ring->real_page_size);
+ }
+ }
+ }
+}
+
+static void atl1e_cal_ring_size(struct atl1e_adapter *adapter, u32 *ring_size)
+{
+ *ring_size = ((u32)(adapter->tx_ring.count *
+ sizeof(struct atl1e_tpd_desc) + 7
+ /* tx ring, qword align */
+ + adapter->rx_ring.real_page_size * AT_PAGE_NUM_PER_QUEUE *
+ adapter->num_rx_queues + 31
+ /* rx ring, 32 bytes align */
+ + (1 + AT_PAGE_NUM_PER_QUEUE * adapter->num_rx_queues) *
+ sizeof(u32) + 3));
+ /* tx, rx cmd, dword align */
+}
+
+static void atl1e_init_ring_resources(struct atl1e_adapter *adapter)
+{
+ struct atl1e_rx_ring *rx_ring = NULL;
+
+ rx_ring = &adapter->rx_ring;
+
+ rx_ring->real_page_size = adapter->rx_ring.page_size
+ + adapter->hw.max_frame_size
+ + ETH_HLEN + VLAN_HLEN
+ + ETH_FCS_LEN;
+ rx_ring->real_page_size = roundup(rx_ring->real_page_size, 32);
+ atl1e_cal_ring_size(adapter, &adapter->ring_size);
+
+ adapter->ring_vir_addr = NULL;
+ adapter->rx_ring.desc = NULL;
+ rwlock_init(&adapter->tx_ring.tx_lock);
+}
+
+/*
+ * Read / Write Ptr Initialize:
+ */
+static void atl1e_init_ring_ptrs(struct atl1e_adapter *adapter)
+{
+ struct atl1e_tx_ring *tx_ring = NULL;
+ struct atl1e_rx_ring *rx_ring = NULL;
+ struct atl1e_rx_page_desc *rx_page_desc = NULL;
+ int i, j;
+
+ tx_ring = &adapter->tx_ring;
+ rx_ring = &adapter->rx_ring;
+ rx_page_desc = rx_ring->rx_page_desc;
+
+ tx_ring->next_to_use = 0;
+ atomic_set(&tx_ring->next_to_clean, 0);
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ rx_page_desc[i].rx_using = 0;
+ rx_page_desc[i].rx_nxseq = 0;
+ for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) {
+ *rx_page_desc[i].rx_page[j].write_offset_addr = 0;
+ rx_page_desc[i].rx_page[j].read_offset = 0;
+ }
+ }
+}
+
+/*
+ * atl1e_free_ring_resources - Free Tx / RX descriptor Resources
+ * @adapter: board private structure
+ *
+ * Free all transmit software resources
+ */
+static void atl1e_free_ring_resources(struct atl1e_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+
+ atl1e_clean_tx_ring(adapter);
+ atl1e_clean_rx_ring(adapter);
+
+ if (adapter->ring_vir_addr) {
+ pci_free_consistent(pdev, adapter->ring_size,
+ adapter->ring_vir_addr, adapter->ring_dma);
+ adapter->ring_vir_addr = NULL;
+ }
+
+ if (adapter->tx_ring.tx_buffer) {
+ kfree(adapter->tx_ring.tx_buffer);
+ adapter->tx_ring.tx_buffer = NULL;
+ }
+}
+
+/*
+ * atl1e_setup_mem_resources - allocate Tx / RX descriptor resources
+ * @adapter: board private structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static int atl1e_setup_ring_resources(struct atl1e_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ struct atl1e_tx_ring *tx_ring;
+ struct atl1e_rx_ring *rx_ring;
+ struct atl1e_rx_page_desc *rx_page_desc;
+ int size, i, j;
+ u32 offset = 0;
+ int err = 0;
+
+ if (adapter->ring_vir_addr != NULL)
+ return 0; /* alloced already */
+
+ tx_ring = &adapter->tx_ring;
+ rx_ring = &adapter->rx_ring;
+
+ /* real ring DMA buffer */
+
+ size = adapter->ring_size;
+ adapter->ring_vir_addr = pci_alloc_consistent(pdev,
+ adapter->ring_size, &adapter->ring_dma);
+
+ if (adapter->ring_vir_addr == NULL) {
+ netdev_err(adapter->netdev,
+ "pci_alloc_consistent failed, size = D%d\n", size);
+ return -ENOMEM;
+ }
+
+ memset(adapter->ring_vir_addr, 0, adapter->ring_size);
+
+ rx_page_desc = rx_ring->rx_page_desc;
+
+ /* Init TPD Ring */
+ tx_ring->dma = roundup(adapter->ring_dma, 8);
+ offset = tx_ring->dma - adapter->ring_dma;
+ tx_ring->desc = adapter->ring_vir_addr + offset;
+ size = sizeof(struct atl1e_tx_buffer) * (tx_ring->count);
+ tx_ring->tx_buffer = kzalloc(size, GFP_KERNEL);
+ if (tx_ring->tx_buffer == NULL) {
+ netdev_err(adapter->netdev, "kzalloc failed, size = D%d\n",
+ size);
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ /* Init RXF-Pages */
+ offset += (sizeof(struct atl1e_tpd_desc) * tx_ring->count);
+ offset = roundup(offset, 32);
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) {
+ rx_page_desc[i].rx_page[j].dma =
+ adapter->ring_dma + offset;
+ rx_page_desc[i].rx_page[j].addr =
+ adapter->ring_vir_addr + offset;
+ offset += rx_ring->real_page_size;
+ }
+ }
+
+ /* Init CMB dma address */
+ tx_ring->cmb_dma = adapter->ring_dma + offset;
+ tx_ring->cmb = adapter->ring_vir_addr + offset;
+ offset += sizeof(u32);
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) {
+ rx_page_desc[i].rx_page[j].write_offset_dma =
+ adapter->ring_dma + offset;
+ rx_page_desc[i].rx_page[j].write_offset_addr =
+ adapter->ring_vir_addr + offset;
+ offset += sizeof(u32);
+ }
+ }
+
+ if (unlikely(offset > adapter->ring_size)) {
+ netdev_err(adapter->netdev, "offset(%d) > ring size(%d) !!\n",
+ offset, adapter->ring_size);
+ err = -1;
+ goto failed;
+ }
+
+ return 0;
+failed:
+ if (adapter->ring_vir_addr != NULL) {
+ pci_free_consistent(pdev, adapter->ring_size,
+ adapter->ring_vir_addr, adapter->ring_dma);
+ adapter->ring_vir_addr = NULL;
+ }
+ return err;
+}
+
+static inline void atl1e_configure_des_ring(const 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_rx_page_desc *rx_page_desc = NULL;
+ int i, j;
+
+ AT_WRITE_REG(hw, REG_DESC_BASE_ADDR_HI,
+ (u32)((adapter->ring_dma & AT_DMA_HI_ADDR_MASK) >> 32));
+ AT_WRITE_REG(hw, REG_TPD_BASE_ADDR_LO,
+ (u32)((tx_ring->dma) & AT_DMA_LO_ADDR_MASK));
+ AT_WRITE_REG(hw, REG_TPD_RING_SIZE, (u16)(tx_ring->count));
+ AT_WRITE_REG(hw, REG_HOST_TX_CMB_LO,
+ (u32)((tx_ring->cmb_dma) & AT_DMA_LO_ADDR_MASK));
+
+ rx_page_desc = rx_ring->rx_page_desc;
+ /* RXF Page Physical address / Page Length */
+ for (i = 0; i < AT_MAX_RECEIVE_QUEUE; i++) {
+ AT_WRITE_REG(hw, atl1e_rx_page_hi_addr_regs[i],
+ (u32)((adapter->ring_dma &
+ AT_DMA_HI_ADDR_MASK) >> 32));
+ for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) {
+ u32 page_phy_addr;
+ u32 offset_phy_addr;
+
+ page_phy_addr = rx_page_desc[i].rx_page[j].dma;
+ offset_phy_addr =
+ rx_page_desc[i].rx_page[j].write_offset_dma;
+
+ AT_WRITE_REG(hw, atl1e_rx_page_lo_addr_regs[i][j],
+ page_phy_addr & AT_DMA_LO_ADDR_MASK);
+ AT_WRITE_REG(hw, atl1e_rx_page_write_offset_regs[i][j],
+ offset_phy_addr & AT_DMA_LO_ADDR_MASK);
+ AT_WRITE_REGB(hw, atl1e_rx_page_vld_regs[i][j], 1);
+ }
+ }
+ /* Page Length */
+ AT_WRITE_REG(hw, REG_HOST_RXFPAGE_SIZE, rx_ring->page_size);
+ /* Load all of base address above */
+ AT_WRITE_REG(hw, REG_LOAD_PTR, 1);
+}
+
+static inline void atl1e_configure_tx(struct atl1e_adapter *adapter)
+{
+ struct atl1e_hw *hw = (struct atl1e_hw *)&adapter->hw;
+ u32 dev_ctrl_data = 0;
+ u32 max_pay_load = 0;
+ u32 jumbo_thresh = 0;
+ u32 extra_size = 0; /* Jumbo frame threshold in QWORD unit */
+
+ /* configure TXQ param */
+ if (hw->nic_type != athr_l2e_revB) {
+ extra_size = ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN;
+ if (hw->max_frame_size <= 1500) {
+ jumbo_thresh = hw->max_frame_size + extra_size;
+ } else if (hw->max_frame_size < 6*1024) {
+ jumbo_thresh =
+ (hw->max_frame_size + extra_size) * 2 / 3;
+ } else {
+ jumbo_thresh = (hw->max_frame_size + extra_size) / 2;
+ }
+ AT_WRITE_REG(hw, REG_TX_EARLY_TH, (jumbo_thresh + 7) >> 3);
+ }
+
+ dev_ctrl_data = AT_READ_REG(hw, REG_DEVICE_CTRL);
+
+ max_pay_load = ((dev_ctrl_data >> DEVICE_CTRL_MAX_PAYLOAD_SHIFT)) &
+ DEVICE_CTRL_MAX_PAYLOAD_MASK;
+
+ hw->dmaw_block = min_t(u32, max_pay_load, hw->dmaw_block);
+
+ max_pay_load = ((dev_ctrl_data >> DEVICE_CTRL_MAX_RREQ_SZ_SHIFT)) &
+ DEVICE_CTRL_MAX_RREQ_SZ_MASK;
+ hw->dmar_block = min_t(u32, max_pay_load, hw->dmar_block);
+
+ if (hw->nic_type != athr_l2e_revB)
+ AT_WRITE_REGW(hw, REG_TXQ_CTRL + 2,
+ atl1e_pay_load_size[hw->dmar_block]);
+ /* enable TXQ */
+ AT_WRITE_REGW(hw, REG_TXQ_CTRL,
+ (((u16)hw->tpd_burst & TXQ_CTRL_NUM_TPD_BURST_MASK)
+ << TXQ_CTRL_NUM_TPD_BURST_SHIFT)
+ | TXQ_CTRL_ENH_MODE | TXQ_CTRL_EN);
+}
+
+static inline void atl1e_configure_rx(struct atl1e_adapter *adapter)
+{
+ struct atl1e_hw *hw = (struct atl1e_hw *)&adapter->hw;
+ u32 rxf_len = 0;
+ u32 rxf_low = 0;
+ u32 rxf_high = 0;
+ u32 rxf_thresh_data = 0;
+ u32 rxq_ctrl_data = 0;
+
+ if (hw->nic_type != athr_l2e_revB) {
+ AT_WRITE_REGW(hw, REG_RXQ_JMBOSZ_RRDTIM,
+ (u16)((hw->rx_jumbo_th & RXQ_JMBOSZ_TH_MASK) <<
+ RXQ_JMBOSZ_TH_SHIFT |
+ (1 & RXQ_JMBO_LKAH_MASK) <<
+ RXQ_JMBO_LKAH_SHIFT));
+
+ rxf_len = AT_READ_REG(hw, REG_SRAM_RXF_LEN);
+ rxf_high = rxf_len * 4 / 5;
+ rxf_low = rxf_len / 5;
+ rxf_thresh_data = ((rxf_high & RXQ_RXF_PAUSE_TH_HI_MASK)
+ << RXQ_RXF_PAUSE_TH_HI_SHIFT) |
+ ((rxf_low & RXQ_RXF_PAUSE_TH_LO_MASK)
+ << RXQ_RXF_PAUSE_TH_LO_SHIFT);
+
+ AT_WRITE_REG(hw, REG_RXQ_RXF_PAUSE_THRESH, rxf_thresh_data);
+ }
+
+ /* RRS */
+ AT_WRITE_REG(hw, REG_IDT_TABLE, hw->indirect_tab);
+ AT_WRITE_REG(hw, REG_BASE_CPU_NUMBER, hw->base_cpu);
+
+ if (hw->rrs_type & atl1e_rrs_ipv4)
+ rxq_ctrl_data |= RXQ_CTRL_HASH_TYPE_IPV4;
+
+ if (hw->rrs_type & atl1e_rrs_ipv4_tcp)
+ rxq_ctrl_data |= RXQ_CTRL_HASH_TYPE_IPV4_TCP;
+
+ if (hw->rrs_type & atl1e_rrs_ipv6)
+ rxq_ctrl_data |= RXQ_CTRL_HASH_TYPE_IPV6;
+
+ if (hw->rrs_type & atl1e_rrs_ipv6_tcp)
+ rxq_ctrl_data |= RXQ_CTRL_HASH_TYPE_IPV6_TCP;
+
+ if (hw->rrs_type != atl1e_rrs_disable)
+ rxq_ctrl_data |=
+ (RXQ_CTRL_HASH_ENABLE | RXQ_CTRL_RSS_MODE_MQUESINT);
+
+ rxq_ctrl_data |= RXQ_CTRL_IPV6_XSUM_VERIFY_EN | RXQ_CTRL_PBA_ALIGN_32 |
+ RXQ_CTRL_CUT_THRU_EN | RXQ_CTRL_EN;
+
+ AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq_ctrl_data);
+}
+
+static inline void atl1e_configure_dma(struct atl1e_adapter *adapter)
+{
+ struct atl1e_hw *hw = &adapter->hw;
+ u32 dma_ctrl_data = 0;
+
+ dma_ctrl_data = DMA_CTRL_RXCMB_EN;
+ dma_ctrl_data |= (((u32)hw->dmar_block) & DMA_CTRL_DMAR_BURST_LEN_MASK)
+ << DMA_CTRL_DMAR_BURST_LEN_SHIFT;
+ dma_ctrl_data |= (((u32)hw->dmaw_block) & DMA_CTRL_DMAW_BURST_LEN_MASK)
+ << DMA_CTRL_DMAW_BURST_LEN_SHIFT;
+ dma_ctrl_data |= DMA_CTRL_DMAR_REQ_PRI | DMA_CTRL_DMAR_OUT_ORDER;
+ dma_ctrl_data |= (((u32)hw->dmar_dly_cnt) & DMA_CTRL_DMAR_DLY_CNT_MASK)
+ << DMA_CTRL_DMAR_DLY_CNT_SHIFT;
+ dma_ctrl_data |= (((u32)hw->dmaw_dly_cnt) & DMA_CTRL_DMAW_DLY_CNT_MASK)
+ << DMA_CTRL_DMAW_DLY_CNT_SHIFT;
+
+ AT_WRITE_REG(hw, REG_DMA_CTRL, dma_ctrl_data);
+}
+
+static void atl1e_setup_mac_ctrl(struct atl1e_adapter *adapter)
+{
+ u32 value;
+ struct atl1e_hw *hw = &adapter->hw;
+ struct net_device *netdev = adapter->netdev;
+
+ /* Config MAC CTRL Register */
+ value = MAC_CTRL_TX_EN |
+ MAC_CTRL_RX_EN ;
+
+ if (FULL_DUPLEX == adapter->link_duplex)
+ value |= MAC_CTRL_DUPLX;
+
+ value |= ((u32)((SPEED_1000 == adapter->link_speed) ?
+ MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100) <<
+ MAC_CTRL_SPEED_SHIFT);
+ value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW);
+
+ value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD);
+ value |= (((u32)adapter->hw.preamble_len &
+ MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT);
+
+ __atl1e_vlan_mode(netdev->features, &value);
+
+ value |= MAC_CTRL_BC_EN;
+ if (netdev->flags & IFF_PROMISC)
+ value |= MAC_CTRL_PROMIS_EN;
+ if (netdev->flags & IFF_ALLMULTI)
+ value |= MAC_CTRL_MC_ALL_EN;
+
+ AT_WRITE_REG(hw, REG_MAC_CTRL, value);
+}
+
+/*
+ * atl1e_configure - Configure Transmit&Receive Unit after Reset
+ * @adapter: board private structure
+ *
+ * Configure the Tx /Rx unit of the MAC after a reset.
+ */
+static int atl1e_configure(struct atl1e_adapter *adapter)
+{
+ struct atl1e_hw *hw = &adapter->hw;
+
+ u32 intr_status_data = 0;
+
+ /* clear interrupt status */
+ AT_WRITE_REG(hw, REG_ISR, ~0);
+
+ /* 1. set MAC Address */
+ atl1e_hw_set_mac_addr(hw);
+
+ /* 2. Init the Multicast HASH table done by set_muti */
+
+ /* 3. Clear any WOL status */
+ AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
+
+ /* 4. Descripter Ring BaseMem/Length/Read ptr/Write ptr
+ * TPD Ring/SMB/RXF0 Page CMBs, they use the same
+ * High 32bits memory */
+ atl1e_configure_des_ring(adapter);
+
+ /* 5. set Interrupt Moderator Timer */
+ AT_WRITE_REGW(hw, REG_IRQ_MODU_TIMER_INIT, hw->imt);
+ AT_WRITE_REGW(hw, REG_IRQ_MODU_TIMER2_INIT, hw->imt);
+ AT_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_LED_MODE |
+ MASTER_CTRL_ITIMER_EN | MASTER_CTRL_ITIMER2_EN);
+
+ /* 6. rx/tx threshold to trig interrupt */
+ AT_WRITE_REGW(hw, REG_TRIG_RRD_THRESH, hw->rrd_thresh);
+ AT_WRITE_REGW(hw, REG_TRIG_TPD_THRESH, hw->tpd_thresh);
+ AT_WRITE_REGW(hw, REG_TRIG_RXTIMER, hw->rx_count_down);
+ AT_WRITE_REGW(hw, REG_TRIG_TXTIMER, hw->tx_count_down);
+
+ /* 7. set Interrupt Clear Timer */
+ AT_WRITE_REGW(hw, REG_CMBDISDMA_TIMER, hw->ict);
+
+ /* 8. set MTU */
+ AT_WRITE_REG(hw, REG_MTU, hw->max_frame_size + ETH_HLEN +
+ VLAN_HLEN + ETH_FCS_LEN);
+
+ /* 9. config TXQ early tx threshold */
+ atl1e_configure_tx(adapter);
+
+ /* 10. config RXQ */
+ atl1e_configure_rx(adapter);
+
+ /* 11. config DMA Engine */
+ atl1e_configure_dma(adapter);
+
+ /* 12. smb timer to trig interrupt */
+ AT_WRITE_REG(hw, REG_SMB_STAT_TIMER, hw->smb_timer);
+
+ intr_status_data = AT_READ_REG(hw, REG_ISR);
+ if (unlikely((intr_status_data & ISR_PHY_LINKDOWN) != 0)) {
+ netdev_err(adapter->netdev,
+ "atl1e_configure failed, PCIE phy link down\n");
+ return -1;
+ }
+
+ AT_WRITE_REG(hw, REG_ISR, 0x7fffffff);
+ return 0;
+}
+
+/*
+ * atl1e_get_stats - Get System Network Statistics
+ * @netdev: network interface device structure
+ *
+ * Returns the address of the device statistics structure.
+ * The statistics are actually updated from the timer callback.
+ */
+static struct net_device_stats *atl1e_get_stats(struct net_device *netdev)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ struct atl1e_hw_stats *hw_stats = &adapter->hw_stats;
+ struct net_device_stats *net_stats = &netdev->stats;
+
+ net_stats->rx_packets = hw_stats->rx_ok;
+ net_stats->tx_packets = hw_stats->tx_ok;
+ net_stats->rx_bytes = hw_stats->rx_byte_cnt;
+ net_stats->tx_bytes = hw_stats->tx_byte_cnt;
+ net_stats->multicast = hw_stats->rx_mcast;
+ net_stats->collisions = hw_stats->tx_1_col +
+ hw_stats->tx_2_col * 2 +
+ hw_stats->tx_late_col + hw_stats->tx_abort_col;
+
+ net_stats->rx_errors = hw_stats->rx_frag + hw_stats->rx_fcs_err +
+ hw_stats->rx_len_err + hw_stats->rx_sz_ov +
+ hw_stats->rx_rrd_ov + hw_stats->rx_align_err;
+ net_stats->rx_fifo_errors = hw_stats->rx_rxf_ov;
+ net_stats->rx_length_errors = hw_stats->rx_len_err;
+ net_stats->rx_crc_errors = hw_stats->rx_fcs_err;
+ net_stats->rx_frame_errors = hw_stats->rx_align_err;
+ net_stats->rx_over_errors = hw_stats->rx_rrd_ov + hw_stats->rx_rxf_ov;
+
+ net_stats->rx_missed_errors = hw_stats->rx_rrd_ov + hw_stats->rx_rxf_ov;
+
+ net_stats->tx_errors = hw_stats->tx_late_col + hw_stats->tx_abort_col +
+ hw_stats->tx_underrun + hw_stats->tx_trunc;
+ net_stats->tx_fifo_errors = hw_stats->tx_underrun;
+ net_stats->tx_aborted_errors = hw_stats->tx_abort_col;
+ net_stats->tx_window_errors = hw_stats->tx_late_col;
+
+ return net_stats;
+}
+
+static void atl1e_update_hw_stats(struct atl1e_adapter *adapter)
+{
+ u16 hw_reg_addr = 0;
+ unsigned long *stats_item = NULL;
+
+ /* update rx status */
+ hw_reg_addr = REG_MAC_RX_STATUS_BIN;
+ stats_item = &adapter->hw_stats.rx_ok;
+ while (hw_reg_addr <= REG_MAC_RX_STATUS_END) {
+ *stats_item += AT_READ_REG(&adapter->hw, hw_reg_addr);
+ stats_item++;
+ hw_reg_addr += 4;
+ }
+ /* update tx status */
+ hw_reg_addr = REG_MAC_TX_STATUS_BIN;
+ stats_item = &adapter->hw_stats.tx_ok;
+ while (hw_reg_addr <= REG_MAC_TX_STATUS_END) {
+ *stats_item += AT_READ_REG(&adapter->hw, hw_reg_addr);
+ stats_item++;
+ hw_reg_addr += 4;
+ }
+}
+
+static inline void atl1e_clear_phy_int(struct atl1e_adapter *adapter)
+{
+ u16 phy_data;
+
+ spin_lock(&adapter->mdio_lock);
+ atl1e_read_phy_reg(&adapter->hw, MII_INT_STATUS, &phy_data);
+ spin_unlock(&adapter->mdio_lock);
+}
+
+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_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);
+
+ while (next_to_clean != hw_next_to_clean) {
+ tx_buffer = &tx_ring->tx_buffer[next_to_clean];
+ if (tx_buffer->dma) {
+ if (tx_buffer->flags & ATL1E_TX_PCIMAP_SINGLE)
+ pci_unmap_single(adapter->pdev, tx_buffer->dma,
+ tx_buffer->length, PCI_DMA_TODEVICE);
+ else if (tx_buffer->flags & ATL1E_TX_PCIMAP_PAGE)
+ pci_unmap_page(adapter->pdev, tx_buffer->dma,
+ tx_buffer->length, PCI_DMA_TODEVICE);
+ tx_buffer->dma = 0;
+ }
+
+ if (tx_buffer->skb) {
+ dev_kfree_skb_irq(tx_buffer->skb);
+ tx_buffer->skb = NULL;
+ }
+
+ if (++next_to_clean == tx_ring->count)
+ next_to_clean = 0;
+ }
+
+ atomic_set(&tx_ring->next_to_clean, next_to_clean);
+
+ if (netif_queue_stopped(adapter->netdev) &&
+ netif_carrier_ok(adapter->netdev)) {
+ netif_wake_queue(adapter->netdev);
+ }
+
+ return true;
+}
+
+/*
+ * atl1e_intr - Interrupt Handler
+ * @irq: interrupt number
+ * @data: pointer to a network interface device structure
+ * @pt_regs: CPU registers structure
+ */
+static irqreturn_t atl1e_intr(int irq, void *data)
+{
+ struct net_device *netdev = data;
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ struct atl1e_hw *hw = &adapter->hw;
+ int max_ints = AT_MAX_INT_WORK;
+ int handled = IRQ_NONE;
+ u32 status;
+
+ do {
+ status = AT_READ_REG(hw, REG_ISR);
+ if ((status & IMR_NORMAL_MASK) == 0 ||
+ (status & ISR_DIS_INT) != 0) {
+ if (max_ints != AT_MAX_INT_WORK)
+ handled = IRQ_HANDLED;
+ break;
+ }
+ /* link event */
+ if (status & ISR_GPHY)
+ atl1e_clear_phy_int(adapter);
+ /* Ack ISR */
+ AT_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT);
+
+ handled = IRQ_HANDLED;
+ /* check if PCIE PHY Link down */
+ if (status & ISR_PHY_LINKDOWN) {
+ netdev_err(adapter->netdev,
+ "pcie phy linkdown %x\n", status);
+ if (netif_running(adapter->netdev)) {
+ /* reset MAC */
+ atl1e_irq_reset(adapter);
+ schedule_work(&adapter->reset_task);
+ break;
+ }
+ }
+
+ /* check if DMA read/write error */
+ if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) {
+ netdev_err(adapter->netdev,
+ "PCIE DMA RW error (status = 0x%x)\n",
+ status);
+ atl1e_irq_reset(adapter);
+ schedule_work(&adapter->reset_task);
+ break;
+ }
+
+ if (status & ISR_SMB)
+ atl1e_update_hw_stats(adapter);
+
+ /* link event */
+ if (status & (ISR_GPHY | ISR_MANUAL)) {
+ netdev->stats.tx_carrier_errors++;
+ atl1e_link_chg_event(adapter);
+ break;
+ }
+
+ /* transmit event */
+ if (status & ISR_TX_EVENT)
+ atl1e_clean_tx_irq(adapter);
+
+ if (status & ISR_RX_EVENT) {
+ /*
+ * disable rx interrupts, without
+ * the synchronize_irq bit
+ */
+ AT_WRITE_REG(hw, REG_IMR,
+ IMR_NORMAL_MASK & ~ISR_RX_EVENT);
+ AT_WRITE_FLUSH(hw);
+ if (likely(napi_schedule_prep(
+ &adapter->napi)))
+ __napi_schedule(&adapter->napi);
+ }
+ } while (--max_ints > 0);
+ /* re-enable Interrupt*/
+ AT_WRITE_REG(&adapter->hw, REG_ISR, 0);
+
+ return handled;
+}
+
+static inline void atl1e_rx_checksum(struct atl1e_adapter *adapter,
+ struct sk_buff *skb, struct atl1e_recv_ret_status *prrs)
+{
+ u8 *packet = (u8 *)(prrs + 1);
+ struct iphdr *iph;
+ u16 head_len = ETH_HLEN;
+ u16 pkt_flags;
+ u16 err_flags;
+
+ skb_checksum_none_assert(skb);
+ pkt_flags = prrs->pkt_flag;
+ err_flags = prrs->err_flag;
+ if (((pkt_flags & RRS_IS_IPV4) || (pkt_flags & RRS_IS_IPV6)) &&
+ ((pkt_flags & RRS_IS_TCP) || (pkt_flags & RRS_IS_UDP))) {
+ if (pkt_flags & RRS_IS_IPV4) {
+ if (pkt_flags & RRS_IS_802_3)
+ head_len += 8;
+ iph = (struct iphdr *) (packet + head_len);
+ if (iph->frag_off != 0 && !(pkt_flags & RRS_IS_IP_DF))
+ goto hw_xsum;
+ }
+ if (!(err_flags & (RRS_ERR_IP_CSUM | RRS_ERR_L4_CSUM))) {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ return;
+ }
+ }
+
+hw_xsum :
+ return;
+}
+
+static struct atl1e_rx_page *atl1e_get_rx_page(struct atl1e_adapter *adapter,
+ u8 que)
+{
+ struct atl1e_rx_page_desc *rx_page_desc =
+ (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]);
+}
+
+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_page_desc *rx_page_desc =
+ (struct atl1e_rx_page_desc *) rx_ring->rx_page_desc;
+ struct sk_buff *skb = NULL;
+ struct atl1e_rx_page *rx_page = atl1e_get_rx_page(adapter, que);
+ u32 packet_size, write_offset;
+ struct atl1e_recv_ret_status *prrs;
+
+ write_offset = *(rx_page->write_offset_addr);
+ if (likely(rx_page->read_offset < write_offset)) {
+ do {
+ if (*work_done >= work_to_do)
+ break;
+ (*work_done)++;
+ /* get new packet's rrs */
+ prrs = (struct atl1e_recv_ret_status *) (rx_page->addr +
+ rx_page->read_offset);
+ /* check sequence number */
+ if (prrs->seq_num != rx_page_desc[que].rx_nxseq) {
+ netdev_err(netdev,
+ "rx sequence number error (rx=%d) (expect=%d)\n",
+ prrs->seq_num,
+ rx_page_desc[que].rx_nxseq);
+ rx_page_desc[que].rx_nxseq++;
+ /* just for debug use */
+ AT_WRITE_REG(&adapter->hw, REG_DEBUG_DATA0,
+ (((u32)prrs->seq_num) << 16) |
+ rx_page_desc[que].rx_nxseq);
+ goto fatal_err;
+ }
+ rx_page_desc[que].rx_nxseq++;
+
+ /* error packet */
+ if (prrs->pkt_flag & RRS_IS_ERR_FRAME) {
+ if (prrs->err_flag & (RRS_ERR_BAD_CRC |
+ RRS_ERR_DRIBBLE | RRS_ERR_CODE |
+ RRS_ERR_TRUNC)) {
+ /* hardware error, discard this packet*/
+ netdev_err(netdev,
+ "rx packet desc error %x\n",
+ *((u32 *)prrs + 1));
+ goto skip_pkt;
+ }
+ }
+
+ packet_size = ((prrs->word1 >> RRS_PKT_SIZE_SHIFT) &
+ RRS_PKT_SIZE_MASK) - 4; /* CRC */
+ skb = netdev_alloc_skb_ip_align(netdev, packet_size);
+ if (skb == NULL) {
+ netdev_warn(netdev,
+ "Memory squeeze, deferring packet\n");
+ goto skip_pkt;
+ }
+ memcpy(skb->data, (u8 *)(prrs + 1), packet_size);
+ skb_put(skb, packet_size);
+ skb->protocol = eth_type_trans(skb, netdev);
+ atl1e_rx_checksum(adapter, skb, prrs);
+
+ if (prrs->pkt_flag & RRS_IS_VLAN_TAG) {
+ u16 vlan_tag = (prrs->vtag >> 4) |
+ ((prrs->vtag & 7) << 13) |
+ ((prrs->vtag & 8) << 9);
+ netdev_dbg(netdev,
+ "RXD VLAN TAG<RRD>=0x%04x\n",
+ prrs->vtag);
+ __vlan_hwaccel_put_tag(skb, vlan_tag);
+ }
+ netif_receive_skb(skb);
+
+skip_pkt:
+ /* skip current packet whether it's ok or not. */
+ rx_page->read_offset +=
+ (((u32)((prrs->word1 >> RRS_PKT_SIZE_SHIFT) &
+ RRS_PKT_SIZE_MASK) +
+ sizeof(struct atl1e_recv_ret_status) + 31) &
+ 0xFFFFFFE0);
+
+ if (rx_page->read_offset >= rx_ring->page_size) {
+ /* mark this page clean */
+ u16 reg_addr;
+ u8 rx_using;
+
+ rx_page->read_offset =
+ *(rx_page->write_offset_addr) = 0;
+ rx_using = rx_page_desc[que].rx_using;
+ reg_addr =
+ atl1e_rx_page_vld_regs[que][rx_using];
+ AT_WRITE_REGB(&adapter->hw, reg_addr, 1);
+ rx_page_desc[que].rx_using ^= 1;
+ rx_page = atl1e_get_rx_page(adapter, que);
+ }
+ write_offset = *(rx_page->write_offset_addr);
+ } while (rx_page->read_offset < write_offset);
+ }
+
+ return;
+
+fatal_err:
+ if (!test_bit(__AT_DOWN, &adapter->flags))
+ schedule_work(&adapter->reset_task);
+}
+
+/*
+ * atl1e_clean - NAPI Rx polling callback
+ * @adapter: board private structure
+ */
+static int atl1e_clean(struct napi_struct *napi, int budget)
+{
+ struct atl1e_adapter *adapter =
+ container_of(napi, struct atl1e_adapter, napi);
+ u32 imr_data;
+ int work_done = 0;
+
+ /* Keep link state information with original netdev */
+ if (!netif_carrier_ok(adapter->netdev))
+ goto quit_polling;
+
+ atl1e_clean_rx_irq(adapter, 0, &work_done, budget);
+
+ /* If no Tx and not enough Rx work done, exit the polling mode */
+ if (work_done < budget) {
+quit_polling:
+ napi_complete(napi);
+ imr_data = AT_READ_REG(&adapter->hw, REG_IMR);
+ AT_WRITE_REG(&adapter->hw, REG_IMR, imr_data | ISR_RX_EVENT);
+ /* test debug */
+ if (test_bit(__AT_DOWN, &adapter->flags)) {
+ atomic_dec(&adapter->irq_sem);
+ netdev_err(adapter->netdev,
+ "atl1e_clean is called when AT_DOWN\n");
+ }
+ /* reenable RX intr */
+ /*atl1e_irq_enable(adapter); */
+
+ }
+ return work_done;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+
+/*
+ * 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.
+ */
+static void atl1e_netpoll(struct net_device *netdev)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+
+ disable_irq(adapter->pdev->irq);
+ atl1e_intr(adapter->pdev->irq, netdev);
+ enable_irq(adapter->pdev->irq);
+}
+#endif
+
+static inline u16 atl1e_tpd_avail(struct atl1e_adapter *adapter)
+{
+ struct atl1e_tx_ring *tx_ring = &adapter->tx_ring;
+ u16 next_to_use = 0;
+ u16 next_to_clean = 0;
+
+ next_to_clean = atomic_read(&tx_ring->next_to_clean);
+ next_to_use = tx_ring->next_to_use;
+
+ return (u16)(next_to_clean > next_to_use) ?
+ (next_to_clean - next_to_use - 1) :
+ (tx_ring->count + next_to_clean - next_to_use - 1);
+}
+
+/*
+ * get next usable tpd
+ * Note: should call atl1e_tdp_avail to make sure
+ * there is enough tpd to use
+ */
+static struct atl1e_tpd_desc *atl1e_get_tpd(struct atl1e_adapter *adapter)
+{
+ struct atl1e_tx_ring *tx_ring = &adapter->tx_ring;
+ u16 next_to_use = 0;
+
+ next_to_use = tx_ring->next_to_use;
+ if (++tx_ring->next_to_use == tx_ring->count)
+ 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];
+}
+
+static struct atl1e_tx_buffer *
+atl1e_get_tx_buffer(struct atl1e_adapter *adapter, struct atl1e_tpd_desc *tpd)
+{
+ struct atl1e_tx_ring *tx_ring = &adapter->tx_ring;
+
+ return &tx_ring->tx_buffer[tpd - tx_ring->desc];
+}
+
+/* Calculate the transmit packet descript needed*/
+static u16 atl1e_cal_tdp_req(const struct sk_buff *skb)
+{
+ int i = 0;
+ u16 tpd_req = 1;
+ u16 fg_size = 0;
+ u16 proto_hdr_len = 0;
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ fg_size = skb_frag_size(&skb_shinfo(skb)->frags[i]);
+ tpd_req += ((fg_size + MAX_TX_BUF_LEN - 1) >> MAX_TX_BUF_SHIFT);
+ }
+
+ if (skb_is_gso(skb)) {
+ if (skb->protocol == htons(ETH_P_IP) ||
+ (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6)) {
+ proto_hdr_len = skb_transport_offset(skb) +
+ tcp_hdrlen(skb);
+ if (proto_hdr_len < skb_headlen(skb)) {
+ tpd_req += ((skb_headlen(skb) - proto_hdr_len +
+ MAX_TX_BUF_LEN - 1) >>
+ MAX_TX_BUF_SHIFT);
+ }
+ }
+
+ }
+ return tpd_req;
+}
+
+static int atl1e_tso_csum(struct atl1e_adapter *adapter,
+ struct sk_buff *skb, struct atl1e_tpd_desc *tpd)
+{
+ u8 hdr_len;
+ u32 real_len;
+ unsigned short offload_type;
+ int err;
+
+ if (skb_is_gso(skb)) {
+ if (skb_header_cloned(skb)) {
+ err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+ if (unlikely(err))
+ return -1;
+ }
+ offload_type = skb_shinfo(skb)->gso_type;
+
+ if (offload_type & SKB_GSO_TCPV4) {
+ real_len = (((unsigned char *)ip_hdr(skb) - skb->data)
+ + ntohs(ip_hdr(skb)->tot_len));
+
+ if (real_len < skb->len)
+ pskb_trim(skb, real_len);
+
+ hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb));
+ if (unlikely(skb->len == hdr_len)) {
+ /* only xsum need */
+ netdev_warn(adapter->netdev,
+ "IPV4 tso with zero data??\n");
+ goto check_sum;
+ } else {
+ ip_hdr(skb)->check = 0;
+ ip_hdr(skb)->tot_len = 0;
+ tcp_hdr(skb)->check = ~csum_tcpudp_magic(
+ ip_hdr(skb)->saddr,
+ ip_hdr(skb)->daddr,
+ 0, IPPROTO_TCP, 0);
+ tpd->word3 |= (ip_hdr(skb)->ihl &
+ TDP_V4_IPHL_MASK) <<
+ TPD_V4_IPHL_SHIFT;
+ tpd->word3 |= ((tcp_hdrlen(skb) >> 2) &
+ TPD_TCPHDRLEN_MASK) <<
+ TPD_TCPHDRLEN_SHIFT;
+ tpd->word3 |= ((skb_shinfo(skb)->gso_size) &
+ TPD_MSS_MASK) << TPD_MSS_SHIFT;
+ tpd->word3 |= 1 << TPD_SEGMENT_EN_SHIFT;
+ }
+ return 0;
+ }
+ }
+
+check_sum:
+ if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
+ u8 css, cso;
+
+ cso = skb_checksum_start_offset(skb);
+ if (unlikely(cso & 0x1)) {
+ netdev_err(adapter->netdev,
+ "payload offset should not ant event number\n");
+ return -1;
+ } else {
+ css = cso + skb->csum_offset;
+ tpd->word3 |= (cso & TPD_PLOADOFFSET_MASK) <<
+ TPD_PLOADOFFSET_SHIFT;
+ tpd->word3 |= (css & TPD_CCSUMOFFSET_MASK) <<
+ TPD_CCSUMOFFSET_SHIFT;
+ tpd->word3 |= 1 << TPD_CC_SEGMENT_EN_SHIFT;
+ }
+ }
+
+ return 0;
+}
+
+static void atl1e_tx_map(struct atl1e_adapter *adapter,
+ struct sk_buff *skb, struct atl1e_tpd_desc *tpd)
+{
+ struct atl1e_tpd_desc *use_tpd = NULL;
+ struct atl1e_tx_buffer *tx_buffer = NULL;
+ u16 buf_len = skb_headlen(skb);
+ u16 map_len = 0;
+ u16 mapped_len = 0;
+ u16 hdr_len = 0;
+ u16 nr_frags;
+ u16 f;
+ int segment;
+
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ segment = (tpd->word3 >> TPD_SEGMENT_EN_SHIFT) & TPD_SEGMENT_EN_MASK;
+ if (segment) {
+ /* TSO */
+ map_len = hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+ use_tpd = tpd;
+
+ tx_buffer = atl1e_get_tx_buffer(adapter, use_tpd);
+ tx_buffer->length = map_len;
+ tx_buffer->dma = pci_map_single(adapter->pdev,
+ skb->data, hdr_len, PCI_DMA_TODEVICE);
+ ATL1E_SET_PCIMAP_TYPE(tx_buffer, ATL1E_TX_PCIMAP_SINGLE);
+ mapped_len += map_len;
+ use_tpd->buffer_addr = cpu_to_le64(tx_buffer->dma);
+ use_tpd->word2 = (use_tpd->word2 & (~TPD_BUFLEN_MASK)) |
+ ((cpu_to_le32(tx_buffer->length) &
+ TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT);
+ }
+
+ while (mapped_len < buf_len) {
+ /* mapped_len == 0, means we should use the first tpd,
+ which is given by caller */
+ if (mapped_len == 0) {
+ use_tpd = tpd;
+ } else {
+ use_tpd = atl1e_get_tpd(adapter);
+ memcpy(use_tpd, tpd, sizeof(struct atl1e_tpd_desc));
+ }
+ tx_buffer = atl1e_get_tx_buffer(adapter, use_tpd);
+ tx_buffer->skb = NULL;
+
+ tx_buffer->length = map_len =
+ ((buf_len - mapped_len) >= MAX_TX_BUF_LEN) ?
+ MAX_TX_BUF_LEN : (buf_len - mapped_len);
+ tx_buffer->dma =
+ pci_map_single(adapter->pdev, skb->data + mapped_len,
+ map_len, PCI_DMA_TODEVICE);
+ ATL1E_SET_PCIMAP_TYPE(tx_buffer, ATL1E_TX_PCIMAP_SINGLE);
+ mapped_len += map_len;
+ use_tpd->buffer_addr = cpu_to_le64(tx_buffer->dma);
+ use_tpd->word2 = (use_tpd->word2 & (~TPD_BUFLEN_MASK)) |
+ ((cpu_to_le32(tx_buffer->length) &
+ TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT);
+ }
+
+ for (f = 0; f < nr_frags; f++) {
+ const struct skb_frag_struct *frag;
+ u16 i;
+ u16 seg_num;
+
+ frag = &skb_shinfo(skb)->frags[f];
+ buf_len = skb_frag_size(frag);
+
+ seg_num = (buf_len + MAX_TX_BUF_LEN - 1) / MAX_TX_BUF_LEN;
+ for (i = 0; i < seg_num; i++) {
+ use_tpd = atl1e_get_tpd(adapter);
+ memcpy(use_tpd, tpd, sizeof(struct atl1e_tpd_desc));
+
+ tx_buffer = atl1e_get_tx_buffer(adapter, use_tpd);
+ BUG_ON(tx_buffer->skb);
+
+ tx_buffer->skb = NULL;
+ tx_buffer->length =
+ (buf_len > MAX_TX_BUF_LEN) ?
+ MAX_TX_BUF_LEN : buf_len;
+ buf_len -= tx_buffer->length;
+
+ tx_buffer->dma = skb_frag_dma_map(&adapter->pdev->dev,
+ frag,
+ (i * MAX_TX_BUF_LEN),
+ tx_buffer->length,
+ DMA_TO_DEVICE);
+ ATL1E_SET_PCIMAP_TYPE(tx_buffer, ATL1E_TX_PCIMAP_PAGE);
+ use_tpd->buffer_addr = cpu_to_le64(tx_buffer->dma);
+ use_tpd->word2 = (use_tpd->word2 & (~TPD_BUFLEN_MASK)) |
+ ((cpu_to_le32(tx_buffer->length) &
+ TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT);
+ }
+ }
+
+ if ((tpd->word3 >> TPD_SEGMENT_EN_SHIFT) & TPD_SEGMENT_EN_MASK)
+ /* note this one is a tcp header */
+ tpd->word3 |= 1 << TPD_HDRFLAG_SHIFT;
+ /* The last tpd */
+
+ use_tpd->word3 |= 1 << TPD_EOP_SHIFT;
+ /* The last buffer info contain the skb address,
+ so it will be free after unmap */
+ tx_buffer->skb = skb;
+}
+
+static void atl1e_tx_queue(struct atl1e_adapter *adapter, u16 count,
+ struct atl1e_tpd_desc *tpd)
+{
+ struct atl1e_tx_ring *tx_ring = &adapter->tx_ring;
+ /* Force memory writes to complete before letting h/w
+ * know there are new descriptors to fetch. (Only
+ * applicable for weak-ordered memory model archs,
+ * such as IA-64). */
+ wmb();
+ AT_WRITE_REG(&adapter->hw, REG_MB_TPD_PROD_IDX, tx_ring->next_to_use);
+}
+
+static netdev_tx_t atl1e_xmit_frame(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ unsigned long flags;
+ u16 tpd_req = 1;
+ struct atl1e_tpd_desc *tpd;
+
+ if (test_bit(__AT_DOWN, &adapter->flags)) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ if (unlikely(skb->len <= 0)) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+ tpd_req = atl1e_cal_tdp_req(skb);
+ if (!spin_trylock_irqsave(&adapter->tx_lock, flags))
+ return NETDEV_TX_LOCKED;
+
+ if (atl1e_tpd_avail(adapter) < tpd_req) {
+ /* no enough descriptor, just stop queue */
+ netif_stop_queue(netdev);
+ spin_unlock_irqrestore(&adapter->tx_lock, flags);
+ return NETDEV_TX_BUSY;
+ }
+
+ tpd = atl1e_get_tpd(adapter);
+
+ if (vlan_tx_tag_present(skb)) {
+ u16 vlan_tag = vlan_tx_tag_get(skb);
+ u16 atl1e_vlan_tag;
+
+ tpd->word3 |= 1 << TPD_INS_VL_TAG_SHIFT;
+ AT_VLAN_TAG_TO_TPD_TAG(vlan_tag, atl1e_vlan_tag);
+ tpd->word2 |= (atl1e_vlan_tag & TPD_VLANTAG_MASK) <<
+ TPD_VLAN_SHIFT;
+ }
+
+ if (skb->protocol == htons(ETH_P_8021Q))
+ tpd->word3 |= 1 << TPD_VL_TAGGED_SHIFT;
+
+ if (skb_network_offset(skb) != ETH_HLEN)
+ tpd->word3 |= 1 << TPD_ETHTYPE_SHIFT; /* 802.3 frame */
+
+ /* do TSO and check sum */
+ if (atl1e_tso_csum(adapter, skb, tpd) != 0) {
+ spin_unlock_irqrestore(&adapter->tx_lock, flags);
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ atl1e_tx_map(adapter, skb, tpd);
+ atl1e_tx_queue(adapter, tpd_req, tpd);
+
+ netdev->trans_start = jiffies; /* NETIF_F_LLTX driver :( */
+ spin_unlock_irqrestore(&adapter->tx_lock, flags);
+ return NETDEV_TX_OK;
+}
+
+static void atl1e_free_irq(struct atl1e_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+
+ free_irq(adapter->pdev->irq, netdev);
+
+ if (adapter->have_msi)
+ pci_disable_msi(adapter->pdev);
+}
+
+static int atl1e_request_irq(struct atl1e_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ struct net_device *netdev = adapter->netdev;
+ int flags = 0;
+ int err = 0;
+
+ adapter->have_msi = true;
+ err = pci_enable_msi(adapter->pdev);
+ if (err) {
+ netdev_dbg(adapter->netdev,
+ "Unable to allocate MSI interrupt Error: %d\n", err);
+ adapter->have_msi = false;
+ } else
+ netdev->irq = pdev->irq;
+
+
+ if (!adapter->have_msi)
+ flags |= IRQF_SHARED;
+ err = request_irq(adapter->pdev->irq, atl1e_intr, flags,
+ netdev->name, netdev);
+ if (err) {
+ netdev_dbg(adapter->netdev,
+ "Unable to allocate interrupt Error: %d\n", err);
+ if (adapter->have_msi)
+ pci_disable_msi(adapter->pdev);
+ return err;
+ }
+ netdev_dbg(adapter->netdev, "atl1e_request_irq OK\n");
+ return err;
+}
+
+int atl1e_up(struct atl1e_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ int err = 0;
+ u32 val;
+
+ /* hardware has been reset, we need to reload some things */
+ err = atl1e_init_hw(&adapter->hw);
+ if (err) {
+ err = -EIO;
+ return err;
+ }
+ atl1e_init_ring_ptrs(adapter);
+ atl1e_set_multi(netdev);
+ atl1e_restore_vlan(adapter);
+
+ if (atl1e_configure(adapter)) {
+ err = -EIO;
+ goto err_up;
+ }
+
+ clear_bit(__AT_DOWN, &adapter->flags);
+ napi_enable(&adapter->napi);
+ atl1e_irq_enable(adapter);
+ val = AT_READ_REG(&adapter->hw, REG_MASTER_CTRL);
+ AT_WRITE_REG(&adapter->hw, REG_MASTER_CTRL,
+ val | MASTER_CTRL_MANUAL_INT);
+
+err_up:
+ return err;
+}
+
+void atl1e_down(struct atl1e_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+
+ /* signal that we're down so the interrupt handler does not
+ * reschedule our watchdog timer */
+ set_bit(__AT_DOWN, &adapter->flags);
+
+#if defined(NETIF_F_LLTX) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39))
+ netif_stop_queue(netdev);
+#else
+ netif_tx_disable(netdev);
+#endif
+
+ /* reset MAC to disable all RX/TX */
+ atl1e_reset_hw(&adapter->hw);
+ msleep(1);
+
+ napi_disable(&adapter->napi);
+ atl1e_del_timer(adapter);
+ atl1e_irq_disable(adapter);
+
+ netif_carrier_off(netdev);
+ adapter->link_speed = SPEED_0;
+ adapter->link_duplex = -1;
+ atl1e_clean_tx_ring(adapter);
+ atl1e_clean_rx_ring(adapter);
+}
+
+/*
+ * atl1e_open - Called when a network interface is made active
+ * @netdev: network interface device structure
+ *
+ * Returns 0 on success, negative value on failure
+ *
+ * The open entry point is called when a network interface is made
+ * active by the system (IFF_UP). At this point all resources needed
+ * for transmit and receive operations are allocated, the interrupt
+ * handler is registered with the OS, the watchdog timer is started,
+ * and the stack is notified that the interface is ready.
+ */
+static int atl1e_open(struct net_device *netdev)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ int err;
+
+ /* disallow open during test */
+ if (test_bit(__AT_TESTING, &adapter->flags))
+ return -EBUSY;
+
+ /* allocate rx/tx dma buffer & descriptors */
+ atl1e_init_ring_resources(adapter);
+ err = atl1e_setup_ring_resources(adapter);
+ if (unlikely(err))
+ return err;
+
+ err = atl1e_request_irq(adapter);
+ if (unlikely(err))
+ goto err_req_irq;
+
+ err = atl1e_up(adapter);
+ if (unlikely(err))
+ goto err_up;
+
+ return 0;
+
+err_up:
+ atl1e_free_irq(adapter);
+err_req_irq:
+ atl1e_free_ring_resources(adapter);
+ atl1e_reset_hw(&adapter->hw);
+
+ return err;
+}
+
+/*
+ * atl1e_close - Disables a network interface
+ * @netdev: network interface device structure
+ *
+ * Returns 0, this is not allowed to fail
+ *
+ * The close entry point is called when an interface is de-activated
+ * by the OS. The hardware is still under the drivers control, but
+ * needs to be disabled. A global MAC reset is issued to stop the
+ * hardware, and all transmit and receive resources are freed.
+ */
+static int atl1e_close(struct net_device *netdev)
+{
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+
+ WARN_ON(test_bit(__AT_RESETTING, &adapter->flags));
+ atl1e_down(adapter);
+ atl1e_free_irq(adapter);
+ atl1e_free_ring_resources(adapter);
+
+ return 0;
+}
+
+static int atl1e_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ struct atl1e_hw *hw = &adapter->hw;
+ u32 ctrl = 0;
+ u32 mac_ctrl_data = 0;
+ u32 wol_ctrl_data = 0;
+ u16 mii_advertise_data = 0;
+ u16 mii_bmsr_data = 0;
+ u16 mii_intr_status_data = 0;
+ u32 wufc = adapter->wol;
+ u32 i;
+#ifdef CONFIG_PM
+ int retval = 0;
+#endif
+
+ if (netif_running(netdev)) {
+ WARN_ON(test_bit(__AT_RESETTING, &adapter->flags));
+ atl1e_down(adapter);
+ }
+ netif_device_detach(netdev);
+
+#ifdef CONFIG_PM
+ retval = pci_save_state(pdev);
+ if (retval)
+ return retval;
+#endif
+
+ 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);
+
+ mii_advertise_data = ADVERTISE_10HALF;
+
+ if ((atl1e_write_phy_reg(hw, MII_CTRL1000, 0) != 0) ||
+ (atl1e_write_phy_reg(hw,
+ MII_ADVERTISE, mii_advertise_data) != 0) ||
+ (atl1e_phy_commit(hw)) != 0) {
+ netdev_dbg(adapter->netdev, "set phy register failed\n");
+ goto wol_dis;
+ }
+
+ hw->phy_configured = false; /* re-init PHY when resume */
+
+ /* turn on magic packet wol */
+ if (wufc & AT_WUFC_MAG)
+ wol_ctrl_data |= WOL_MAGIC_EN | WOL_MAGIC_PME_EN;
+
+ if (wufc & AT_WUFC_LNKC) {
+ /* if orignal link status is link, just wait for retrive link */
+ if (mii_bmsr_data & BMSR_LSTATUS) {
+ for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) {
+ msleep(100);
+ atl1e_read_phy_reg(hw, MII_BMSR,
+ (u16 *)&mii_bmsr_data);
+ if (mii_bmsr_data & BMSR_LSTATUS)
+ break;
+ }
+
+ if ((mii_bmsr_data & BMSR_LSTATUS) == 0)
+ netdev_dbg(adapter->netdev,
+ "Link may change when suspend\n");
+ }
+ wol_ctrl_data |= WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN;
+ /* only link up can wake up */
+ if (atl1e_write_phy_reg(hw, MII_INT_CTRL, 0x400) != 0) {
+ netdev_dbg(adapter->netdev,
+ "read write phy register failed\n");
+ goto wol_dis;
+ }
+ }
+ /* clear phy interrupt */
+ atl1e_read_phy_reg(hw, MII_INT_STATUS, &mii_intr_status_data);
+ /* Config MAC Ctrl register */
+ mac_ctrl_data = MAC_CTRL_RX_EN;
+ /* set to 10/100M halt duplex */
+ mac_ctrl_data |= MAC_CTRL_SPEED_10_100 << MAC_CTRL_SPEED_SHIFT;
+ mac_ctrl_data |= (((u32)adapter->hw.preamble_len &
+ MAC_CTRL_PRMLEN_MASK) <<
+ MAC_CTRL_PRMLEN_SHIFT);
+
+ __atl1e_vlan_mode(netdev->features, &mac_ctrl_data);
+
+ /* magic packet maybe Broadcast&multicast&Unicast frame */
+ if (wufc & AT_WUFC_MAG)
+ mac_ctrl_data |= MAC_CTRL_BC_EN;
+
+ netdev_dbg(adapter->netdev, "suspend MAC=0x%x\n",
+ mac_ctrl_data);
+
+ AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl_data);
+ AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data);
+ /* pcie patch */
+ ctrl = AT_READ_REG(hw, REG_PCIE_PHYMISC);
+ ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
+ AT_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl);
+ pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
+ goto suspend_exit;
+ }
+wol_dis:
+
+ /* WOL disabled */
+ AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
+
+ /* pcie patch */
+ ctrl = AT_READ_REG(hw, REG_PCIE_PHYMISC);
+ ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
+ AT_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl);
+
+ atl1e_force_ps(hw);
+ hw->phy_configured = false; /* re-init PHY when resume */
+
+ pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
+
+suspend_exit:
+
+ if (netif_running(netdev))
+ atl1e_free_irq(adapter);
+
+ pci_disable_device(pdev);
+
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int atl1e_resume(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+ u32 err;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ netdev_err(adapter->netdev,
+ "Cannot enable PCI device from suspend\n");
+ return err;
+ }
+
+ pci_set_master(pdev);
+
+ AT_READ_REG(&adapter->hw, REG_WOL_CTRL); /* clear WOL status */
+
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ pci_enable_wake(pdev, PCI_D3cold, 0);
+
+ AT_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0);
+
+ if (netif_running(netdev)) {
+ err = atl1e_request_irq(adapter);
+ if (err)
+ return err;
+ }
+
+ atl1e_reset_hw(&adapter->hw);
+
+ if (netif_running(netdev))
+ atl1e_up(adapter);
+
+ netif_device_attach(netdev);
+
+ return 0;
+}
+#endif
+
+static void atl1e_shutdown(struct pci_dev *pdev)
+{
+ atl1e_suspend(pdev, PMSG_SUSPEND);
+}
+
+static const struct net_device_ops atl1e_netdev_ops = {
+ .ndo_open = atl1e_open,
+ .ndo_stop = atl1e_close,
+ .ndo_start_xmit = atl1e_xmit_frame,
+ .ndo_get_stats = atl1e_get_stats,
+ .ndo_set_rx_mode = atl1e_set_multi,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = atl1e_set_mac_addr,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39))
+ .ndo_fix_features = atl1e_fix_features,
+ .ndo_set_features = atl1e_set_features,
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */
+ .ndo_change_mtu = atl1e_change_mtu,
+ .ndo_do_ioctl = atl1e_ioctl,
+ .ndo_tx_timeout = atl1e_tx_timeout,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = atl1e_netpoll,
+#endif
+
+};
+
+static int atl1e_init_netdev(struct net_device *netdev, struct pci_dev *pdev)
+{
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+ pci_set_drvdata(pdev, netdev);
+
+ netdev->irq = pdev->irq;
+ netdev_attach_ops(netdev, &atl1e_netdev_ops);
+
+ netdev->watchdog_timeo = AT_TX_WATCHDOG;
+ atl1e_set_ethtool_ops(netdev);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39))
+ netdev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO |
+ NETIF_F_HW_VLAN_RX;
+ netdev->features = netdev->hw_features | NETIF_F_LLTX |
+ NETIF_F_HW_VLAN_TX;
+#else
+ netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO |
+ NETIF_F_HW_VLAN_RX | NETIF_F_LLTX | NETIF_F_HW_VLAN_TX;
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */
+
+ return 0;
+}
+
+/*
+ * atl1e_probe - Device Initialization Routine
+ * @pdev: PCI device information struct
+ * @ent: entry in atl1e_pci_tbl
+ *
+ * Returns 0 on success, negative on failure
+ *
+ * atl1e_probe initializes an adapter identified by a pci_dev structure.
+ * The OS initialization, configuring of the adapter private structure,
+ * and a hardware reset occur.
+ */
+static int __devinit atl1e_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *netdev;
+ struct atl1e_adapter *adapter = NULL;
+ static int cards_found;
+
+ int err = 0;
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "cannot enable PCI device\n");
+ return err;
+ }
+
+ /*
+ * The atl1e chip can DMA to 64-bit addresses, but it uses a single
+ * shared register for the high 32 bits, so only a single, aligned,
+ * 4 GB physical address range can be used at a time.
+ *
+ * Supporting 64-bit DMA on this hardware is more trouble than it's
+ * worth. It is far easier to limit to 32-bit DMA than update
+ * various kernel subsystems to support the mechanics required by a
+ * fixed-high-32-bit system.
+ */
+ if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) ||
+ (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) != 0)) {
+ dev_err(&pdev->dev, "No usable DMA configuration,aborting\n");
+ goto err_dma;
+ }
+
+ err = pci_request_regions(pdev, atl1e_driver_name);
+ if (err) {
+ dev_err(&pdev->dev, "cannot obtain PCI resources\n");
+ goto err_pci_reg;
+ }
+
+ pci_set_master(pdev);
+
+ netdev = alloc_etherdev(sizeof(struct atl1e_adapter));
+ if (netdev == NULL) {
+ err = -ENOMEM;
+ dev_err(&pdev->dev, "etherdev alloc failed\n");
+ goto err_alloc_etherdev;
+ }
+
+ err = atl1e_init_netdev(netdev, pdev);
+ if (err) {
+ netdev_err(netdev, "init netdevice failed\n");
+ goto err_init_netdev;
+ }
+ adapter = netdev_priv(netdev);
+ adapter->bd_number = cards_found;
+ adapter->netdev = netdev;
+ adapter->pdev = pdev;
+ adapter->hw.adapter = adapter;
+ adapter->hw.hw_addr = pci_iomap(pdev, BAR_0, 0);
+ if (!adapter->hw.hw_addr) {
+ err = -EIO;
+ netdev_err(netdev, "cannot map device registers\n");
+ goto err_ioremap;
+ }
+ netdev->base_addr = (unsigned long)adapter->hw.hw_addr;
+
+ /* init mii data */
+ adapter->mii.dev = netdev;
+ adapter->mii.mdio_read = atl1e_mdio_read;
+ adapter->mii.mdio_write = atl1e_mdio_write;
+ adapter->mii.phy_id_mask = 0x1f;
+ adapter->mii.reg_num_mask = MDIO_REG_ADDR_MASK;
+
+ netif_napi_add(netdev, &adapter->napi, atl1e_clean, 64);
+
+ init_timer(&adapter->phy_config_timer);
+ adapter->phy_config_timer.function = atl1e_phy_config;
+ adapter->phy_config_timer.data = (unsigned long) adapter;
+
+ /* get user settings */
+ atl1e_check_options(adapter);
+ /*
+ * Mark all PCI regions associated with PCI device
+ * pdev as being reserved by owner atl1e_driver_name
+ * Enables bus-mastering on the device and calls
+ * pcibios_set_master to do the needed arch specific settings
+ */
+ atl1e_setup_pcicmd(pdev);
+ /* setup the private structure */
+ err = atl1e_sw_init(adapter);
+ if (err) {
+ netdev_err(netdev, "net device private data init failed\n");
+ goto err_sw_init;
+ }
+
+ /* Init GPHY as early as possible due to power saving issue */
+ atl1e_phy_init(&adapter->hw);
+ /* reset the controller to
+ * put the device in a known good starting state */
+ err = atl1e_reset_hw(&adapter->hw);
+ if (err) {
+ err = -EIO;
+ goto err_reset;
+ }
+
+ if (atl1e_read_mac_addr(&adapter->hw) != 0) {
+ err = -EIO;
+ netdev_err(netdev, "get mac address failed\n");
+ goto err_eeprom;
+ }
+
+ memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len);
+ memcpy(netdev->perm_addr, adapter->hw.mac_addr, netdev->addr_len);
+ netdev_dbg(netdev, "mac address : %pM\n", adapter->hw.mac_addr);
+
+ INIT_WORK(&adapter->reset_task, atl1e_reset_task);
+ INIT_WORK(&adapter->link_chg_task, atl1e_link_chg_task);
+ err = register_netdev(netdev);
+ if (err) {
+ netdev_err(netdev, "register netdevice failed\n");
+ goto err_register;
+ }
+
+ /* assume we have no link for now */
+ netif_stop_queue(netdev);
+ netif_carrier_off(netdev);
+
+ cards_found++;
+
+ return 0;
+
+err_reset:
+err_register:
+err_sw_init:
+err_eeprom:
+ iounmap(adapter->hw.hw_addr);
+err_init_netdev:
+err_ioremap:
+ free_netdev(netdev);
+err_alloc_etherdev:
+ pci_release_regions(pdev);
+err_pci_reg:
+err_dma:
+ pci_disable_device(pdev);
+ return err;
+}
+
+/*
+ * atl1e_remove - Device Removal Routine
+ * @pdev: PCI device information struct
+ *
+ * atl1e_remove is called by the PCI subsystem to alert the driver
+ * that it should release a PCI device. The could be caused by a
+ * Hot-Plug event, or because the driver is going to be removed from
+ * memory.
+ */
+static void __devexit atl1e_remove(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+
+ /*
+ * flush_scheduled work may reschedule our watchdog task, so
+ * explicitly disable watchdog tasks from being rescheduled
+ */
+ set_bit(__AT_DOWN, &adapter->flags);
+
+ atl1e_del_timer(adapter);
+ atl1e_cancel_work(adapter);
+
+ unregister_netdev(netdev);
+ atl1e_free_ring_resources(adapter);
+ atl1e_force_ps(&adapter->hw);
+ iounmap(adapter->hw.hw_addr);
+ pci_release_regions(pdev);
+ free_netdev(netdev);
+ pci_disable_device(pdev);
+}
+
+/*
+ * atl1e_io_error_detected - called when PCI error is detected
+ * @pdev: Pointer to PCI device
+ * @state: The current pci connection state
+ *
+ * This function is called after a PCI bus error affecting
+ * this device has been detected.
+ */
+static pci_ers_result_t
+atl1e_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+
+ netif_device_detach(netdev);
+
+ if (state == pci_channel_io_perm_failure)
+ return PCI_ERS_RESULT_DISCONNECT;
+
+ if (netif_running(netdev))
+ atl1e_down(adapter);
+
+ pci_disable_device(pdev);
+
+ /* Request a slot slot reset. */
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/*
+ * atl1e_io_slot_reset - called after the pci bus has been reset.
+ * @pdev: Pointer to PCI device
+ *
+ * Restart the card from scratch, as if from a cold-boot. Implementation
+ * resembles the first-half of the e1000_resume routine.
+ */
+static pci_ers_result_t atl1e_io_slot_reset(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+
+ if (pci_enable_device(pdev)) {
+ netdev_err(adapter->netdev,
+ "Cannot re-enable PCI device after reset\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+ pci_set_master(pdev);
+
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ pci_enable_wake(pdev, PCI_D3cold, 0);
+
+ atl1e_reset_hw(&adapter->hw);
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+/*
+ * atl1e_io_resume - called when traffic can start flowing again.
+ * @pdev: Pointer to PCI device
+ *
+ * This callback is called when the error recovery driver tells us that
+ * its OK to resume normal operation. Implementation resembles the
+ * second-half of the atl1e_resume routine.
+ */
+static void atl1e_io_resume(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
+
+ if (netif_running(netdev)) {
+ if (atl1e_up(adapter)) {
+ netdev_err(adapter->netdev,
+ "can't bring device back up after reset\n");
+ return;
+ }
+ }
+
+ netif_device_attach(netdev);
+}
+
+static struct pci_error_handlers atl1e_err_handler = {
+ .error_detected = atl1e_io_error_detected,
+ .slot_reset = atl1e_io_slot_reset,
+ .resume = atl1e_io_resume,
+};
+
+static struct pci_driver atl1e_driver = {
+ .name = atl1e_driver_name,
+ .id_table = atl1e_pci_tbl,
+ .probe = atl1e_probe,
+ .remove = __devexit_p(atl1e_remove),
+ /* Power Management Hooks */
+#ifdef CONFIG_PM
+ .suspend = atl1e_suspend,
+ .resume = atl1e_resume,
+#endif
+ .shutdown = atl1e_shutdown,
+ .err_handler = &atl1e_err_handler
+};
+
+/*
+ * atl1e_init_module - Driver Registration Routine
+ *
+ * atl1e_init_module is the first routine called when the driver is
+ * loaded. All it does is register with the PCI subsystem.
+ */
+static int __init atl1e_init_module(void)
+{
+ return pci_register_driver(&atl1e_driver);
+}
+
+/*
+ * atl1e_exit_module - Driver Exit Cleanup Routine
+ *
+ * atl1e_exit_module is called just before the driver is removed
+ * from memory.
+ */
+static void __exit atl1e_exit_module(void)
+{
+ pci_unregister_driver(&atl1e_driver);
+}
+
+module_init(atl1e_init_module);
+module_exit(atl1e_exit_module);
diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_param.c b/drivers/net/ethernet/atheros/atl1e/atl1e_param.c
new file mode 100644
index 0000000..0ce60b6
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_param.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright(c) 2007 Atheros Corporation. All rights reserved.
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ */
+
+#include <linux/netdevice.h>
+
+#include "atl1e.h"
+
+/* This is the only thing that needs to be changed to adjust the
+ * maximum number of ports that the driver can manage.
+ */
+
+#define ATL1E_MAX_NIC 32
+
+#define OPTION_UNSET -1
+#define OPTION_DISABLED 0
+#define OPTION_ENABLED 1
+
+/* All parameters are treated the same, as an integer array of values.
+ * This macro just reduces the need to repeat the same declaration code
+ * over and over (plus this helps to avoid typo bugs).
+ */
+#define ATL1E_PARAM_INIT { [0 ... ATL1E_MAX_NIC] = OPTION_UNSET }
+
+#define ATL1E_PARAM(x, desc) \
+ static int __devinitdata x[ATL1E_MAX_NIC + 1] = ATL1E_PARAM_INIT; \
+ static unsigned int num_##x; \
+ module_param_array_named(x, x, int, &num_##x, 0); \
+ MODULE_PARM_DESC(x, desc);
+
+/* Transmit Memory count
+ *
+ * Valid Range: 64-2048
+ *
+ * Default Value: 128
+ */
+#define ATL1E_MIN_TX_DESC_CNT 32
+#define ATL1E_MAX_TX_DESC_CNT 1020
+#define ATL1E_DEFAULT_TX_DESC_CNT 128
+ATL1E_PARAM(tx_desc_cnt, "Transmit description count");
+
+/* Receive Memory Block Count
+ *
+ * Valid Range: 16-512
+ *
+ * Default Value: 128
+ */
+#define ATL1E_MIN_RX_MEM_SIZE 8 /* 8KB */
+#define ATL1E_MAX_RX_MEM_SIZE 1024 /* 1MB */
+#define ATL1E_DEFAULT_RX_MEM_SIZE 256 /* 128KB */
+ATL1E_PARAM(rx_mem_size, "memory size of rx buffer(KB)");
+
+/* User Specified MediaType Override
+ *
+ * Valid Range: 0-5
+ * - 0 - auto-negotiate at all supported speeds
+ * - 1 - only link at 100Mbps Full Duplex
+ * - 2 - only link at 100Mbps Half Duplex
+ * - 3 - only link at 10Mbps Full Duplex
+ * - 4 - only link at 10Mbps Half Duplex
+ * Default Value: 0
+ */
+
+ATL1E_PARAM(media_type, "MediaType Select");
+
+/* Interrupt Moderate Timer in units of 2 us
+ *
+ * Valid Range: 10-65535
+ *
+ * Default Value: 45000(90ms)
+ */
+#define INT_MOD_DEFAULT_CNT 100 /* 200us */
+#define INT_MOD_MAX_CNT 65000
+#define INT_MOD_MIN_CNT 50
+ATL1E_PARAM(int_mod_timer, "Interrupt Moderator Timer");
+
+#define AUTONEG_ADV_DEFAULT 0x2F
+#define AUTONEG_ADV_MASK 0x2F
+#define FLOW_CONTROL_DEFAULT FLOW_CONTROL_FULL
+
+#define FLASH_VENDOR_DEFAULT 0
+#define FLASH_VENDOR_MIN 0
+#define FLASH_VENDOR_MAX 2
+
+struct atl1e_option {
+ enum { enable_option, range_option, list_option } type;
+ char *name;
+ char *err;
+ int def;
+ union {
+ struct { /* range_option info */
+ int min;
+ int max;
+ } r;
+ struct { /* list_option info */
+ int nr;
+ struct atl1e_opt_list { int i; char *str; } *p;
+ } l;
+ } arg;
+};
+
+static int __devinit atl1e_validate_option(int *value, struct atl1e_option *opt, struct atl1e_adapter *adapter)
+{
+ if (*value == OPTION_UNSET) {
+ *value = opt->def;
+ return 0;
+ }
+
+ switch (opt->type) {
+ case enable_option:
+ switch (*value) {
+ case OPTION_ENABLED:
+ netdev_info(adapter->netdev,
+ "%s Enabled\n", opt->name);
+ return 0;
+ case OPTION_DISABLED:
+ netdev_info(adapter->netdev,
+ "%s Disabled\n", opt->name);
+ return 0;
+ }
+ break;
+ case range_option:
+ if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) {
+ netdev_info(adapter->netdev, "%s set to %i\n",
+ opt->name, *value);
+ return 0;
+ }
+ break;
+ case list_option:{
+ int i;
+ struct atl1e_opt_list *ent;
+
+ for (i = 0; i < opt->arg.l.nr; i++) {
+ ent = &opt->arg.l.p[i];
+ if (*value == ent->i) {
+ if (ent->str[0] != '\0')
+ netdev_info(adapter->netdev,
+ "%s\n", ent->str);
+ return 0;
+ }
+ }
+ break;
+ }
+ default:
+ BUG();
+ }
+
+ netdev_info(adapter->netdev, "Invalid %s specified (%i) %s\n",
+ opt->name, *value, opt->err);
+ *value = opt->def;
+ return -1;
+}
+
+/*
+ * atl1e_check_options - Range Checking for Command Line Parameters
+ * @adapter: board private structure
+ *
+ * This routine checks all command line parameters for valid user
+ * input. If an invalid value is given, or if no user specified
+ * value exists, a default value is used. The final value is stored
+ * in a variable in the adapter structure.
+ */
+void __devinit atl1e_check_options(struct atl1e_adapter *adapter)
+{
+ int bd = adapter->bd_number;
+
+ if (bd >= ATL1E_MAX_NIC) {
+ netdev_notice(adapter->netdev,
+ "no configuration for board #%i\n", bd);
+ netdev_notice(adapter->netdev,
+ "Using defaults for all values\n");
+ }
+
+ { /* Transmit Ring Size */
+ struct atl1e_option opt = {
+ .type = range_option,
+ .name = "Transmit Ddescription Count",
+ .err = "using default of "
+ __MODULE_STRING(ATL1E_DEFAULT_TX_DESC_CNT),
+ .def = ATL1E_DEFAULT_TX_DESC_CNT,
+ .arg = { .r = { .min = ATL1E_MIN_TX_DESC_CNT,
+ .max = ATL1E_MAX_TX_DESC_CNT} }
+ };
+ int val;
+ if (num_tx_desc_cnt > bd) {
+ val = tx_desc_cnt[bd];
+ atl1e_validate_option(&val, &opt, adapter);
+ adapter->tx_ring.count = (u16) val & 0xFFFC;
+ } else
+ adapter->tx_ring.count = (u16)opt.def;
+ }
+
+ { /* Receive Memory Block Count */
+ struct atl1e_option opt = {
+ .type = range_option,
+ .name = "Memory size of rx buffer(KB)",
+ .err = "using default of "
+ __MODULE_STRING(ATL1E_DEFAULT_RX_MEM_SIZE),
+ .def = ATL1E_DEFAULT_RX_MEM_SIZE,
+ .arg = { .r = { .min = ATL1E_MIN_RX_MEM_SIZE,
+ .max = ATL1E_MAX_RX_MEM_SIZE} }
+ };
+ int val;
+ if (num_rx_mem_size > bd) {
+ val = rx_mem_size[bd];
+ atl1e_validate_option(&val, &opt, adapter);
+ adapter->rx_ring.page_size = (u32)val * 1024;
+ } else {
+ adapter->rx_ring.page_size = (u32)opt.def * 1024;
+ }
+ }
+
+ { /* Interrupt Moderate Timer */
+ struct atl1e_option opt = {
+ .type = range_option,
+ .name = "Interrupt Moderate Timer",
+ .err = "using default of "
+ __MODULE_STRING(INT_MOD_DEFAULT_CNT),
+ .def = INT_MOD_DEFAULT_CNT,
+ .arg = { .r = { .min = INT_MOD_MIN_CNT,
+ .max = INT_MOD_MAX_CNT} }
+ } ;
+ int val;
+ if (num_int_mod_timer > bd) {
+ val = int_mod_timer[bd];
+ atl1e_validate_option(&val, &opt, adapter);
+ adapter->hw.imt = (u16) val;
+ } else
+ adapter->hw.imt = (u16)(opt.def);
+ }
+
+ { /* MediaType */
+ struct atl1e_option opt = {
+ .type = range_option,
+ .name = "Speed/Duplex Selection",
+ .err = "using default of "
+ __MODULE_STRING(MEDIA_TYPE_AUTO_SENSOR),
+ .def = MEDIA_TYPE_AUTO_SENSOR,
+ .arg = { .r = { .min = MEDIA_TYPE_AUTO_SENSOR,
+ .max = MEDIA_TYPE_10M_HALF} }
+ } ;
+ int val;
+ if (num_media_type > bd) {
+ val = media_type[bd];
+ atl1e_validate_option(&val, &opt, adapter);
+ adapter->hw.media_type = (u16) val;
+ } else
+ adapter->hw.media_type = (u16)(opt.def);
+
+ }
+}
diff --git a/drivers/net/ethernet/atheros/atlx/Makefile b/drivers/net/ethernet/atheros/atlx/Makefile
new file mode 100644
index 0000000..e4f6022
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atlx/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_ATL1) += atl1.o
+obj-$(CONFIG_ATL2) += atl2.o
+
diff --git a/drivers/net/ethernet/atheros/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c
new file mode 100644
index 0000000..ba347a7
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atlx/atl1.c
@@ -0,0 +1,3694 @@
+/*
+ * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved.
+ * Copyright(c) 2006 - 2007 Chris Snook <csnook@redhat.com>
+ * Copyright(c) 2006 - 2008 Jay Cliburn <jcliburn@gmail.com>
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * Contact Information:
+ * Xiong Huang <xiong.huang@atheros.com>
+ * Jie Yang <jie.yang@atheros.com>
+ * Chris Snook <csnook@redhat.com>
+ * Jay Cliburn <jcliburn@gmail.com>
+ *
+ * This version is adapted from the Attansic reference driver.
+ *
+ * TODO:
+ * Add more ethtool functions.
+ * Fix abstruse irq enable/disable condition described here:
+ * http://marc.theaimsgroup.com/?l=linux-netdev&m=116398508500553&w=2
+ *
+ * NEEDS TESTING:
+ * VLAN
+ * multicast
+ * promiscuous mode
+ * interrupt coalescing
+ * SMP torture testing
+ */
+
+#include <linux/atomic.h>
+#include <asm/byteorder.h>
+
+#include <linux/compiler.h>
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/hardirq.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/in.h>
+#include <linux/interrupt.h>
+#include <linux/ip.h>
+#include <linux/irqflags.h>
+#include <linux/irqreturn.h>
+#include <linux/jiffies.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/pm.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/tcp.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include <net/checksum.h>
+
+#include "atl1.h"
+
+#define ATLX_DRIVER_VERSION "2.1.3"
+MODULE_AUTHOR("Xiong Huang <xiong.huang@atheros.com>, "
+ "Chris Snook <csnook@redhat.com>, "
+ "Jay Cliburn <jcliburn@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(ATLX_DRIVER_VERSION);
+
+/* Temporary hack for merging atl1 and atl2 */
+#include "atlx.c"
+
+static const struct ethtool_ops atl1_ethtool_ops;
+
+/*
+ * This is the only thing that needs to be changed to adjust the
+ * maximum number of ports that the driver can manage.
+ */
+#define ATL1_MAX_NIC 4
+
+#define OPTION_UNSET -1
+#define OPTION_DISABLED 0
+#define OPTION_ENABLED 1
+
+#define ATL1_PARAM_INIT { [0 ... ATL1_MAX_NIC] = OPTION_UNSET }
+
+/*
+ * Interrupt Moderate Timer in units of 2 us
+ *
+ * Valid Range: 10-65535
+ *
+ * Default Value: 100 (200us)
+ */
+static int __devinitdata int_mod_timer[ATL1_MAX_NIC+1] = ATL1_PARAM_INIT;
+static unsigned int num_int_mod_timer;
+module_param_array_named(int_mod_timer, int_mod_timer, int,
+ &num_int_mod_timer, 0);
+MODULE_PARM_DESC(int_mod_timer, "Interrupt moderator timer");
+
+#define DEFAULT_INT_MOD_CNT 100 /* 200us */
+#define MAX_INT_MOD_CNT 65000
+#define MIN_INT_MOD_CNT 50
+
+struct atl1_option {
+ enum { enable_option, range_option, list_option } type;
+ char *name;
+ char *err;
+ int def;
+ union {
+ struct { /* range_option info */
+ int min;
+ int max;
+ } r;
+ struct { /* list_option info */
+ int nr;
+ struct atl1_opt_list {
+ int i;
+ char *str;
+ } *p;
+ } l;
+ } arg;
+};
+
+static int __devinit atl1_validate_option(int *value, struct atl1_option *opt,
+ struct pci_dev *pdev)
+{
+ if (*value == OPTION_UNSET) {
+ *value = opt->def;
+ return 0;
+ }
+
+ switch (opt->type) {
+ case enable_option:
+ switch (*value) {
+ case OPTION_ENABLED:
+ dev_info(&pdev->dev, "%s enabled\n", opt->name);
+ return 0;
+ case OPTION_DISABLED:
+ dev_info(&pdev->dev, "%s disabled\n", opt->name);
+ return 0;
+ }
+ break;
+ case range_option:
+ if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) {
+ dev_info(&pdev->dev, "%s set to %i\n", opt->name,
+ *value);
+ return 0;
+ }
+ break;
+ case list_option:{
+ int i;
+ struct atl1_opt_list *ent;
+
+ for (i = 0; i < opt->arg.l.nr; i++) {
+ ent = &opt->arg.l.p[i];
+ if (*value == ent->i) {
+ if (ent->str[0] != '\0')
+ dev_info(&pdev->dev, "%s\n",
+ ent->str);
+ return 0;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ dev_info(&pdev->dev, "invalid %s specified (%i) %s\n",
+ opt->name, *value, opt->err);
+ *value = opt->def;
+ return -1;
+}
+
+/*
+ * atl1_check_options - Range Checking for Command Line Parameters
+ * @adapter: board private structure
+ *
+ * This routine checks all command line parameters for valid user
+ * input. If an invalid value is given, or if no user specified
+ * value exists, a default value is used. The final value is stored
+ * in a variable in the adapter structure.
+ */
+static void __devinit atl1_check_options(struct atl1_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ int bd = adapter->bd_number;
+ if (bd >= ATL1_MAX_NIC) {
+ dev_notice(&pdev->dev, "no configuration for board#%i\n", bd);
+ dev_notice(&pdev->dev, "using defaults for all values\n");
+ }
+ { /* Interrupt Moderate Timer */
+ struct atl1_option opt = {
+ .type = range_option,
+ .name = "Interrupt Moderator Timer",
+ .err = "using default of "
+ __MODULE_STRING(DEFAULT_INT_MOD_CNT),
+ .def = DEFAULT_INT_MOD_CNT,
+ .arg = {.r = {.min = MIN_INT_MOD_CNT,
+ .max = MAX_INT_MOD_CNT} }
+ };
+ int val;
+ if (num_int_mod_timer > bd) {
+ val = int_mod_timer[bd];
+ atl1_validate_option(&val, &opt, pdev);
+ adapter->imt = (u16) val;
+ } else
+ adapter->imt = (u16) (opt.def);
+ }
+}
+
+/*
+ * atl1_pci_tbl - PCI Device ID Table
+ */
+static DEFINE_PCI_DEVICE_TABLE(atl1_pci_tbl) = {
+ {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L1)},
+ /* required last entry */
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, atl1_pci_tbl);
+
+static const u32 atl1_default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE |
+ NETIF_MSG_LINK | NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP;
+
+static int debug = -1;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Message level (0=none,...,16=all)");
+
+/*
+ * Reset the transmit and receive units; mask and clear all interrupts.
+ * hw - Struct containing variables accessed by shared code
+ * return : 0 or idle status (if error)
+ */
+static s32 atl1_reset_hw(struct atl1_hw *hw)
+{
+ struct pci_dev *pdev = hw->back->pdev;
+ struct atl1_adapter *adapter = hw->back;
+ u32 icr;
+ int i;
+
+ /*
+ * Clear Interrupt mask to stop board from generating
+ * interrupts & Clear any pending interrupt events
+ */
+ /*
+ * iowrite32(0, hw->hw_addr + REG_IMR);
+ * iowrite32(0xffffffff, hw->hw_addr + REG_ISR);
+ */
+
+ /*
+ * Issue Soft Reset to the MAC. This will reset the chip's
+ * transmit, receive, DMA. It will not effect
+ * the current PCI configuration. The global reset bit is self-
+ * clearing, and should clear within a microsecond.
+ */
+ iowrite32(MASTER_CTRL_SOFT_RST, hw->hw_addr + REG_MASTER_CTRL);
+ ioread32(hw->hw_addr + REG_MASTER_CTRL);
+
+ iowrite16(1, hw->hw_addr + REG_PHY_ENABLE);
+ ioread16(hw->hw_addr + REG_PHY_ENABLE);
+
+ /* delay about 1ms */
+ msleep(1);
+
+ /* Wait at least 10ms for All module to be Idle */
+ for (i = 0; i < 10; i++) {
+ icr = ioread32(hw->hw_addr + REG_IDLE_STATUS);
+ if (!icr)
+ break;
+ /* delay 1 ms */
+ msleep(1);
+ /* FIXME: still the right way to do this? */
+ cpu_relax();
+ }
+
+ if (icr) {
+ if (netif_msg_hw(adapter))
+ dev_dbg(&pdev->dev, "ICR = 0x%x\n", icr);
+ return icr;
+ }
+
+ return 0;
+}
+
+/* function about EEPROM
+ *
+ * check_eeprom_exist
+ * return 0 if eeprom exist
+ */
+static int atl1_check_eeprom_exist(struct atl1_hw *hw)
+{
+ u32 value;
+ value = ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL);
+ if (value & SPI_FLASH_CTRL_EN_VPD) {
+ value &= ~SPI_FLASH_CTRL_EN_VPD;
+ iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL);
+ }
+
+ value = ioread16(hw->hw_addr + REG_PCIE_CAP_LIST);
+ return ((value & 0xFF00) == 0x6C00) ? 0 : 1;
+}
+
+static bool atl1_read_eeprom(struct atl1_hw *hw, u32 offset, u32 *p_value)
+{
+ int i;
+ u32 control;
+
+ if (offset & 3)
+ /* address do not align */
+ return false;
+
+ iowrite32(0, hw->hw_addr + REG_VPD_DATA);
+ control = (offset & VPD_CAP_VPD_ADDR_MASK) << VPD_CAP_VPD_ADDR_SHIFT;
+ iowrite32(control, hw->hw_addr + REG_VPD_CAP);
+ ioread32(hw->hw_addr + REG_VPD_CAP);
+
+ for (i = 0; i < 10; i++) {
+ msleep(2);
+ control = ioread32(hw->hw_addr + REG_VPD_CAP);
+ if (control & VPD_CAP_VPD_FLAG)
+ break;
+ }
+ if (control & VPD_CAP_VPD_FLAG) {
+ *p_value = ioread32(hw->hw_addr + REG_VPD_DATA);
+ return true;
+ }
+ /* timeout */
+ return false;
+}
+
+/*
+ * Reads the value from a PHY register
+ * hw - Struct containing variables accessed by shared code
+ * reg_addr - address of the PHY register to read
+ */
+static s32 atl1_read_phy_reg(struct atl1_hw *hw, u16 reg_addr, u16 *phy_data)
+{
+ u32 val;
+ int i;
+
+ val = ((u32) (reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT |
+ MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW | MDIO_CLK_25_4 <<
+ MDIO_CLK_SEL_SHIFT;
+ iowrite32(val, hw->hw_addr + REG_MDIO_CTRL);
+ ioread32(hw->hw_addr + REG_MDIO_CTRL);
+
+ for (i = 0; i < MDIO_WAIT_TIMES; i++) {
+ udelay(2);
+ val = ioread32(hw->hw_addr + REG_MDIO_CTRL);
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ break;
+ }
+ if (!(val & (MDIO_START | MDIO_BUSY))) {
+ *phy_data = (u16) val;
+ return 0;
+ }
+ return ATLX_ERR_PHY;
+}
+
+#define CUSTOM_SPI_CS_SETUP 2
+#define CUSTOM_SPI_CLK_HI 2
+#define CUSTOM_SPI_CLK_LO 2
+#define CUSTOM_SPI_CS_HOLD 2
+#define CUSTOM_SPI_CS_HI 3
+
+static bool atl1_spi_read(struct atl1_hw *hw, u32 addr, u32 *buf)
+{
+ int i;
+ u32 value;
+
+ iowrite32(0, hw->hw_addr + REG_SPI_DATA);
+ iowrite32(addr, hw->hw_addr + REG_SPI_ADDR);
+
+ value = SPI_FLASH_CTRL_WAIT_READY |
+ (CUSTOM_SPI_CS_SETUP & SPI_FLASH_CTRL_CS_SETUP_MASK) <<
+ SPI_FLASH_CTRL_CS_SETUP_SHIFT | (CUSTOM_SPI_CLK_HI &
+ SPI_FLASH_CTRL_CLK_HI_MASK) <<
+ SPI_FLASH_CTRL_CLK_HI_SHIFT | (CUSTOM_SPI_CLK_LO &
+ SPI_FLASH_CTRL_CLK_LO_MASK) <<
+ SPI_FLASH_CTRL_CLK_LO_SHIFT | (CUSTOM_SPI_CS_HOLD &
+ SPI_FLASH_CTRL_CS_HOLD_MASK) <<
+ SPI_FLASH_CTRL_CS_HOLD_SHIFT | (CUSTOM_SPI_CS_HI &
+ SPI_FLASH_CTRL_CS_HI_MASK) <<
+ SPI_FLASH_CTRL_CS_HI_SHIFT | (1 & SPI_FLASH_CTRL_INS_MASK) <<
+ SPI_FLASH_CTRL_INS_SHIFT;
+
+ iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL);
+
+ value |= SPI_FLASH_CTRL_START;
+ iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL);
+ ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL);
+
+ for (i = 0; i < 10; i++) {
+ msleep(1);
+ value = ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL);
+ if (!(value & SPI_FLASH_CTRL_START))
+ break;
+ }
+
+ if (value & SPI_FLASH_CTRL_START)
+ return false;
+
+ *buf = ioread32(hw->hw_addr + REG_SPI_DATA);
+
+ return true;
+}
+
+/*
+ * get_permanent_address
+ * return 0 if get valid mac address,
+ */
+static int atl1_get_permanent_address(struct atl1_hw *hw)
+{
+ u32 addr[2];
+ u32 i, control;
+ u16 reg;
+ u8 eth_addr[ETH_ALEN];
+ bool key_valid;
+
+ if (is_valid_ether_addr(hw->perm_mac_addr))
+ return 0;
+
+ /* init */
+ addr[0] = addr[1] = 0;
+
+ if (!atl1_check_eeprom_exist(hw)) {
+ reg = 0;
+ key_valid = false;
+ /* Read out all EEPROM content */
+ i = 0;
+ while (1) {
+ if (atl1_read_eeprom(hw, i + 0x100, &control)) {
+ if (key_valid) {
+ if (reg == REG_MAC_STA_ADDR)
+ addr[0] = control;
+ else if (reg == (REG_MAC_STA_ADDR + 4))
+ addr[1] = control;
+ key_valid = false;
+ } else if ((control & 0xff) == 0x5A) {
+ key_valid = true;
+ reg = (u16) (control >> 16);
+ } else
+ break;
+ } else
+ /* read error */
+ break;
+ i += 4;
+ }
+
+ *(u32 *) ð_addr[2] = swab32(addr[0]);
+ *(u16 *) ð_addr[0] = swab16(*(u16 *) &addr[1]);
+ if (is_valid_ether_addr(eth_addr)) {
+ memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN);
+ return 0;
+ }
+ }
+
+ /* see if SPI FLAGS exist ? */
+ addr[0] = addr[1] = 0;
+ reg = 0;
+ key_valid = false;
+ i = 0;
+ while (1) {
+ if (atl1_spi_read(hw, i + 0x1f000, &control)) {
+ if (key_valid) {
+ if (reg == REG_MAC_STA_ADDR)
+ addr[0] = control;
+ else if (reg == (REG_MAC_STA_ADDR + 4))
+ addr[1] = control;
+ key_valid = false;
+ } else if ((control & 0xff) == 0x5A) {
+ key_valid = true;
+ reg = (u16) (control >> 16);
+ } else
+ /* data end */
+ break;
+ } else
+ /* read error */
+ break;
+ i += 4;
+ }
+
+ *(u32 *) ð_addr[2] = swab32(addr[0]);
+ *(u16 *) ð_addr[0] = swab16(*(u16 *) &addr[1]);
+ if (is_valid_ether_addr(eth_addr)) {
+ memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN);
+ return 0;
+ }
+
+ /*
+ * On some motherboards, the MAC address is written by the
+ * BIOS directly to the MAC register during POST, and is
+ * not stored in eeprom. If all else thus far has failed
+ * to fetch the permanent MAC address, try reading it directly.
+ */
+ addr[0] = ioread32(hw->hw_addr + REG_MAC_STA_ADDR);
+ addr[1] = ioread16(hw->hw_addr + (REG_MAC_STA_ADDR + 4));
+ *(u32 *) ð_addr[2] = swab32(addr[0]);
+ *(u16 *) ð_addr[0] = swab16(*(u16 *) &addr[1]);
+ if (is_valid_ether_addr(eth_addr)) {
+ memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Reads the adapter's MAC address from the EEPROM
+ * hw - Struct containing variables accessed by shared code
+ */
+static s32 atl1_read_mac_addr(struct atl1_hw *hw)
+{
+ u16 i;
+
+ if (atl1_get_permanent_address(hw))
+ random_ether_addr(hw->perm_mac_addr);
+
+ for (i = 0; i < ETH_ALEN; i++)
+ hw->mac_addr[i] = hw->perm_mac_addr[i];
+ return 0;
+}
+
+/*
+ * Hashes an address to determine its location in the multicast table
+ * hw - Struct containing variables accessed by shared code
+ * mc_addr - the multicast address to hash
+ *
+ * atl1_hash_mc_addr
+ * purpose
+ * set hash value for a multicast address
+ * hash calcu processing :
+ * 1. calcu 32bit CRC for multicast address
+ * 2. reverse crc with MSB to LSB
+ */
+static u32 atl1_hash_mc_addr(struct atl1_hw *hw, u8 *mc_addr)
+{
+ u32 crc32, value = 0;
+ int i;
+
+ crc32 = ether_crc_le(6, mc_addr);
+ for (i = 0; i < 32; i++)
+ value |= (((crc32 >> i) & 1) << (31 - i));
+
+ return value;
+}
+
+/*
+ * Sets the bit in the multicast table corresponding to the hash value.
+ * hw - Struct containing variables accessed by shared code
+ * hash_value - Multicast address hash value
+ */
+static void atl1_hash_set(struct atl1_hw *hw, u32 hash_value)
+{
+ u32 hash_bit, hash_reg;
+ u32 mta;
+
+ /*
+ * The HASH Table is a register array of 2 32-bit registers.
+ * It is treated like an array of 64 bits. We want to set
+ * bit BitArray[hash_value]. So we figure out what register
+ * the bit is in, read it, OR in the new bit, then write
+ * back the new value. The register is determined by the
+ * upper 7 bits of the hash value and the bit within that
+ * register are determined by the lower 5 bits of the value.
+ */
+ hash_reg = (hash_value >> 31) & 0x1;
+ hash_bit = (hash_value >> 26) & 0x1F;
+ mta = ioread32((hw->hw_addr + REG_RX_HASH_TABLE) + (hash_reg << 2));
+ mta |= (1 << hash_bit);
+ iowrite32(mta, (hw->hw_addr + REG_RX_HASH_TABLE) + (hash_reg << 2));
+}
+
+/*
+ * Writes a value to a PHY register
+ * hw - Struct containing variables accessed by shared code
+ * reg_addr - address of the PHY register to write
+ * data - data to write to the PHY
+ */
+static s32 atl1_write_phy_reg(struct atl1_hw *hw, u32 reg_addr, u16 phy_data)
+{
+ int i;
+ u32 val;
+
+ val = ((u32) (phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT |
+ (reg_addr & MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT |
+ MDIO_SUP_PREAMBLE |
+ MDIO_START | MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT;
+ iowrite32(val, hw->hw_addr + REG_MDIO_CTRL);
+ ioread32(hw->hw_addr + REG_MDIO_CTRL);
+
+ for (i = 0; i < MDIO_WAIT_TIMES; i++) {
+ udelay(2);
+ val = ioread32(hw->hw_addr + REG_MDIO_CTRL);
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ break;
+ }
+
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ return 0;
+
+ return ATLX_ERR_PHY;
+}
+
+/*
+ * Make L001's PHY out of Power Saving State (bug)
+ * hw - Struct containing variables accessed by shared code
+ * when power on, L001's PHY always on Power saving State
+ * (Gigabit Link forbidden)
+ */
+static s32 atl1_phy_leave_power_saving(struct atl1_hw *hw)
+{
+ s32 ret;
+ ret = atl1_write_phy_reg(hw, 29, 0x0029);
+ if (ret)
+ return ret;
+ return atl1_write_phy_reg(hw, 30, 0);
+}
+
+/*
+ * Resets the PHY and make all config validate
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Sets bit 15 and 12 of the MII Control regiser (for F001 bug)
+ */
+static s32 atl1_phy_reset(struct atl1_hw *hw)
+{
+ struct pci_dev *pdev = hw->back->pdev;
+ struct atl1_adapter *adapter = hw->back;
+ s32 ret_val;
+ u16 phy_data;
+
+ if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+ hw->media_type == MEDIA_TYPE_1000M_FULL)
+ phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN;
+ else {
+ switch (hw->media_type) {
+ case MEDIA_TYPE_100M_FULL:
+ phy_data =
+ MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 |
+ MII_CR_RESET;
+ break;
+ case MEDIA_TYPE_100M_HALF:
+ phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
+ break;
+ case MEDIA_TYPE_10M_FULL:
+ phy_data =
+ MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET;
+ break;
+ default:
+ /* MEDIA_TYPE_10M_HALF: */
+ phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
+ break;
+ }
+ }
+
+ ret_val = atl1_write_phy_reg(hw, MII_BMCR, phy_data);
+ if (ret_val) {
+ u32 val;
+ int i;
+ /* pcie serdes link may be down! */
+ if (netif_msg_hw(adapter))
+ dev_dbg(&pdev->dev, "pcie phy link down\n");
+
+ for (i = 0; i < 25; i++) {
+ msleep(1);
+ val = ioread32(hw->hw_addr + REG_MDIO_CTRL);
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ break;
+ }
+
+ if ((val & (MDIO_START | MDIO_BUSY)) != 0) {
+ if (netif_msg_hw(adapter))
+ dev_warn(&pdev->dev,
+ "pcie link down at least 25ms\n");
+ return ret_val;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Configures PHY autoneg and flow control advertisement settings
+ * hw - Struct containing variables accessed by shared code
+ */
+static s32 atl1_phy_setup_autoneg_adv(struct atl1_hw *hw)
+{
+ s32 ret_val;
+ s16 mii_autoneg_adv_reg;
+ s16 mii_1000t_ctrl_reg;
+
+ /* Read the MII Auto-Neg Advertisement Register (Address 4). */
+ mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK;
+
+ /* Read the MII 1000Base-T Control Register (Address 9). */
+ mii_1000t_ctrl_reg = MII_ATLX_CR_1000T_DEFAULT_CAP_MASK;
+
+ /*
+ * First we clear all the 10/100 mb speed bits in the Auto-Neg
+ * Advertisement Register (Address 4) and the 1000 mb speed bits in
+ * the 1000Base-T Control Register (Address 9).
+ */
+ mii_autoneg_adv_reg &= ~MII_AR_SPEED_MASK;
+ mii_1000t_ctrl_reg &= ~MII_ATLX_CR_1000T_SPEED_MASK;
+
+ /*
+ * Need to parse media_type and set up
+ * the appropriate PHY registers.
+ */
+ switch (hw->media_type) {
+ case MEDIA_TYPE_AUTO_SENSOR:
+ mii_autoneg_adv_reg |= (MII_AR_10T_HD_CAPS |
+ MII_AR_10T_FD_CAPS |
+ MII_AR_100TX_HD_CAPS |
+ MII_AR_100TX_FD_CAPS);
+ mii_1000t_ctrl_reg |= MII_ATLX_CR_1000T_FD_CAPS;
+ break;
+
+ case MEDIA_TYPE_1000M_FULL:
+ mii_1000t_ctrl_reg |= MII_ATLX_CR_1000T_FD_CAPS;
+ break;
+
+ case MEDIA_TYPE_100M_FULL:
+ mii_autoneg_adv_reg |= MII_AR_100TX_FD_CAPS;
+ break;
+
+ case MEDIA_TYPE_100M_HALF:
+ mii_autoneg_adv_reg |= MII_AR_100TX_HD_CAPS;
+ break;
+
+ case MEDIA_TYPE_10M_FULL:
+ mii_autoneg_adv_reg |= MII_AR_10T_FD_CAPS;
+ break;
+
+ default:
+ mii_autoneg_adv_reg |= MII_AR_10T_HD_CAPS;
+ break;
+ }
+
+ /* flow control fixed to enable all */
+ mii_autoneg_adv_reg |= (MII_AR_ASM_DIR | MII_AR_PAUSE);
+
+ hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg;
+ hw->mii_1000t_ctrl_reg = mii_1000t_ctrl_reg;
+
+ ret_val = atl1_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = atl1_write_phy_reg(hw, MII_ATLX_CR, mii_1000t_ctrl_reg);
+ if (ret_val)
+ return ret_val;
+
+ return 0;
+}
+
+/*
+ * Configures link settings.
+ * hw - Struct containing variables accessed by shared code
+ * Assumes the hardware has previously been reset and the
+ * transmitter and receiver are not enabled.
+ */
+static s32 atl1_setup_link(struct atl1_hw *hw)
+{
+ struct pci_dev *pdev = hw->back->pdev;
+ struct atl1_adapter *adapter = hw->back;
+ s32 ret_val;
+
+ /*
+ * Options:
+ * PHY will advertise value(s) parsed from
+ * autoneg_advertised and fc
+ * no matter what autoneg is , We will not wait link result.
+ */
+ ret_val = atl1_phy_setup_autoneg_adv(hw);
+ if (ret_val) {
+ if (netif_msg_link(adapter))
+ dev_dbg(&pdev->dev,
+ "error setting up autonegotiation\n");
+ return ret_val;
+ }
+ /* SW.Reset , En-Auto-Neg if needed */
+ ret_val = atl1_phy_reset(hw);
+ if (ret_val) {
+ if (netif_msg_link(adapter))
+ dev_dbg(&pdev->dev, "error resetting phy\n");
+ return ret_val;
+ }
+ hw->phy_configured = true;
+ return ret_val;
+}
+
+static void atl1_init_flash_opcode(struct atl1_hw *hw)
+{
+ if (hw->flash_vendor >= ARRAY_SIZE(flash_table))
+ /* Atmel */
+ hw->flash_vendor = 0;
+
+ /* Init OP table */
+ iowrite8(flash_table[hw->flash_vendor].cmd_program,
+ hw->hw_addr + REG_SPI_FLASH_OP_PROGRAM);
+ iowrite8(flash_table[hw->flash_vendor].cmd_sector_erase,
+ hw->hw_addr + REG_SPI_FLASH_OP_SC_ERASE);
+ iowrite8(flash_table[hw->flash_vendor].cmd_chip_erase,
+ hw->hw_addr + REG_SPI_FLASH_OP_CHIP_ERASE);
+ iowrite8(flash_table[hw->flash_vendor].cmd_rdid,
+ hw->hw_addr + REG_SPI_FLASH_OP_RDID);
+ iowrite8(flash_table[hw->flash_vendor].cmd_wren,
+ hw->hw_addr + REG_SPI_FLASH_OP_WREN);
+ iowrite8(flash_table[hw->flash_vendor].cmd_rdsr,
+ hw->hw_addr + REG_SPI_FLASH_OP_RDSR);
+ iowrite8(flash_table[hw->flash_vendor].cmd_wrsr,
+ hw->hw_addr + REG_SPI_FLASH_OP_WRSR);
+ iowrite8(flash_table[hw->flash_vendor].cmd_read,
+ hw->hw_addr + REG_SPI_FLASH_OP_READ);
+}
+
+/*
+ * Performs basic configuration of the adapter.
+ * hw - Struct containing variables accessed by shared code
+ * Assumes that the controller has previously been reset and is in a
+ * post-reset uninitialized state. Initializes multicast table,
+ * and Calls routines to setup link
+ * Leaves the transmit and receive units disabled and uninitialized.
+ */
+static s32 atl1_init_hw(struct atl1_hw *hw)
+{
+ u32 ret_val = 0;
+
+ /* Zero out the Multicast HASH table */
+ iowrite32(0, hw->hw_addr + REG_RX_HASH_TABLE);
+ /* clear the old settings from the multicast hash table */
+ iowrite32(0, (hw->hw_addr + REG_RX_HASH_TABLE) + (1 << 2));
+
+ atl1_init_flash_opcode(hw);
+
+ if (!hw->phy_configured) {
+ /* enable GPHY LinkChange Interrupt */
+ ret_val = atl1_write_phy_reg(hw, 18, 0xC00);
+ if (ret_val)
+ return ret_val;
+ /* make PHY out of power-saving state */
+ ret_val = atl1_phy_leave_power_saving(hw);
+ if (ret_val)
+ return ret_val;
+ /* Call a subroutine to configure the link */
+ ret_val = atl1_setup_link(hw);
+ }
+ return ret_val;
+}
+
+/*
+ * Detects the current speed and duplex settings of the hardware.
+ * hw - Struct containing variables accessed by shared code
+ * speed - Speed of the connection
+ * duplex - Duplex setting of the connection
+ */
+static s32 atl1_get_speed_and_duplex(struct atl1_hw *hw, u16 *speed, u16 *duplex)
+{
+ struct pci_dev *pdev = hw->back->pdev;
+ struct atl1_adapter *adapter = hw->back;
+ s32 ret_val;
+ u16 phy_data;
+
+ /* ; --- Read PHY Specific Status Register (17) */
+ ret_val = atl1_read_phy_reg(hw, MII_ATLX_PSSR, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ if (!(phy_data & MII_ATLX_PSSR_SPD_DPLX_RESOLVED))
+ return ATLX_ERR_PHY_RES;
+
+ switch (phy_data & MII_ATLX_PSSR_SPEED) {
+ case MII_ATLX_PSSR_1000MBS:
+ *speed = SPEED_1000;
+ break;
+ case MII_ATLX_PSSR_100MBS:
+ *speed = SPEED_100;
+ break;
+ case MII_ATLX_PSSR_10MBS:
+ *speed = SPEED_10;
+ break;
+ default:
+ if (netif_msg_hw(adapter))
+ dev_dbg(&pdev->dev, "error getting speed\n");
+ return ATLX_ERR_PHY_SPEED;
+ break;
+ }
+ if (phy_data & MII_ATLX_PSSR_DPLX)
+ *duplex = FULL_DUPLEX;
+ else
+ *duplex = HALF_DUPLEX;
+
+ return 0;
+}
+
+static void atl1_set_mac_addr(struct atl1_hw *hw)
+{
+ u32 value;
+ /*
+ * 00-0B-6A-F6-00-DC
+ * 0: 6AF600DC 1: 000B
+ * low dword
+ */
+ value = (((u32) hw->mac_addr[2]) << 24) |
+ (((u32) hw->mac_addr[3]) << 16) |
+ (((u32) hw->mac_addr[4]) << 8) | (((u32) hw->mac_addr[5]));
+ iowrite32(value, hw->hw_addr + REG_MAC_STA_ADDR);
+ /* high dword */
+ value = (((u32) hw->mac_addr[0]) << 8) | (((u32) hw->mac_addr[1]));
+ iowrite32(value, (hw->hw_addr + REG_MAC_STA_ADDR) + (1 << 2));
+}
+
+/*
+ * atl1_sw_init - Initialize general software structures (struct atl1_adapter)
+ * @adapter: board private structure to initialize
+ *
+ * atl1_sw_init initializes the Adapter private data structure.
+ * Fields are initialized based on PCI device information and
+ * OS network device settings (MTU size).
+ */
+static int __devinit atl1_sw_init(struct atl1_adapter *adapter)
+{
+ struct atl1_hw *hw = &adapter->hw;
+ struct net_device *netdev = adapter->netdev;
+
+ hw->max_frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+ hw->min_frame_size = ETH_ZLEN + ETH_FCS_LEN;
+
+ adapter->wol = 0;
+ device_set_wakeup_enable(&adapter->pdev->dev, false);
+ adapter->rx_buffer_len = (hw->max_frame_size + 7) & ~7;
+ adapter->ict = 50000; /* 100ms */
+ adapter->link_speed = SPEED_0; /* hardware init */
+ adapter->link_duplex = FULL_DUPLEX;
+
+ hw->phy_configured = false;
+ hw->preamble_len = 7;
+ hw->ipgt = 0x60;
+ hw->min_ifg = 0x50;
+ hw->ipgr1 = 0x40;
+ hw->ipgr2 = 0x60;
+ hw->max_retry = 0xf;
+ hw->lcol = 0x37;
+ hw->jam_ipg = 7;
+ hw->rfd_burst = 8;
+ hw->rrd_burst = 8;
+ hw->rfd_fetch_gap = 1;
+ hw->rx_jumbo_th = adapter->rx_buffer_len / 8;
+ hw->rx_jumbo_lkah = 1;
+ hw->rrd_ret_timer = 16;
+ hw->tpd_burst = 4;
+ hw->tpd_fetch_th = 16;
+ hw->txf_burst = 0x100;
+ hw->tx_jumbo_task_th = (hw->max_frame_size + 7) >> 3;
+ hw->tpd_fetch_gap = 1;
+ hw->rcb_value = atl1_rcb_64;
+ hw->dma_ord = atl1_dma_ord_enh;
+ hw->dmar_block = atl1_dma_req_256;
+ hw->dmaw_block = atl1_dma_req_256;
+ hw->cmb_rrd = 4;
+ hw->cmb_tpd = 4;
+ hw->cmb_rx_timer = 1; /* about 2us */
+ hw->cmb_tx_timer = 1; /* about 2us */
+ hw->smb_timer = 100000; /* about 200ms */
+
+ spin_lock_init(&adapter->lock);
+ spin_lock_init(&adapter->mb_lock);
+
+ return 0;
+}
+
+static int mdio_read(struct net_device *netdev, int phy_id, int reg_num)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ u16 result;
+
+ atl1_read_phy_reg(&adapter->hw, reg_num & 0x1f, &result);
+
+ return result;
+}
+
+static void mdio_write(struct net_device *netdev, int phy_id, int reg_num,
+ int val)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+
+ atl1_write_phy_reg(&adapter->hw, reg_num, val);
+}
+
+/*
+ * atl1_mii_ioctl -
+ * @netdev:
+ * @ifreq:
+ * @cmd:
+ */
+static int atl1_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ unsigned long flags;
+ int retval;
+
+ if (!netif_running(netdev))
+ return -EINVAL;
+
+ spin_lock_irqsave(&adapter->lock, flags);
+ retval = generic_mii_ioctl(&adapter->mii, if_mii(ifr), cmd, NULL);
+ spin_unlock_irqrestore(&adapter->lock, flags);
+
+ return retval;
+}
+
+/*
+ * atl1_setup_mem_resources - allocate Tx / RX descriptor resources
+ * @adapter: board private structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static s32 atl1_setup_ring_resources(struct atl1_adapter *adapter)
+{
+ struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
+ struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring;
+ struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring;
+ struct atl1_ring_header *ring_header = &adapter->ring_header;
+ struct pci_dev *pdev = adapter->pdev;
+ int size;
+ u8 offset = 0;
+
+ size = sizeof(struct atl1_buffer) * (tpd_ring->count + rfd_ring->count);
+ tpd_ring->buffer_info = kzalloc(size, GFP_KERNEL);
+ if (unlikely(!tpd_ring->buffer_info)) {
+ if (netif_msg_drv(adapter))
+ dev_err(&pdev->dev, "kzalloc failed , size = D%d\n",
+ size);
+ goto err_nomem;
+ }
+ rfd_ring->buffer_info =
+ (struct atl1_buffer *)(tpd_ring->buffer_info + tpd_ring->count);
+
+ /*
+ * real ring DMA buffer
+ * each ring/block may need up to 8 bytes for alignment, hence the
+ * additional 40 bytes tacked onto the end.
+ */
+ ring_header->size = size =
+ sizeof(struct tx_packet_desc) * tpd_ring->count
+ + sizeof(struct rx_free_desc) * rfd_ring->count
+ + sizeof(struct rx_return_desc) * rrd_ring->count
+ + sizeof(struct coals_msg_block)
+ + sizeof(struct stats_msg_block)
+ + 40;
+
+ ring_header->desc = pci_alloc_consistent(pdev, ring_header->size,
+ &ring_header->dma);
+ if (unlikely(!ring_header->desc)) {
+ if (netif_msg_drv(adapter))
+ dev_err(&pdev->dev, "pci_alloc_consistent failed\n");
+ goto err_nomem;
+ }
+
+ memset(ring_header->desc, 0, ring_header->size);
+
+ /* init TPD ring */
+ tpd_ring->dma = ring_header->dma;
+ offset = (tpd_ring->dma & 0x7) ? (8 - (ring_header->dma & 0x7)) : 0;
+ tpd_ring->dma += offset;
+ tpd_ring->desc = (u8 *) ring_header->desc + offset;
+ tpd_ring->size = sizeof(struct tx_packet_desc) * tpd_ring->count;
+
+ /* init RFD ring */
+ rfd_ring->dma = tpd_ring->dma + tpd_ring->size;
+ offset = (rfd_ring->dma & 0x7) ? (8 - (rfd_ring->dma & 0x7)) : 0;
+ rfd_ring->dma += offset;
+ rfd_ring->desc = (u8 *) tpd_ring->desc + (tpd_ring->size + offset);
+ rfd_ring->size = sizeof(struct rx_free_desc) * rfd_ring->count;
+
+
+ /* init RRD ring */
+ rrd_ring->dma = rfd_ring->dma + rfd_ring->size;
+ offset = (rrd_ring->dma & 0x7) ? (8 - (rrd_ring->dma & 0x7)) : 0;
+ rrd_ring->dma += offset;
+ rrd_ring->desc = (u8 *) rfd_ring->desc + (rfd_ring->size + offset);
+ rrd_ring->size = sizeof(struct rx_return_desc) * rrd_ring->count;
+
+
+ /* init CMB */
+ adapter->cmb.dma = rrd_ring->dma + rrd_ring->size;
+ offset = (adapter->cmb.dma & 0x7) ? (8 - (adapter->cmb.dma & 0x7)) : 0;
+ adapter->cmb.dma += offset;
+ adapter->cmb.cmb = (struct coals_msg_block *)
+ ((u8 *) rrd_ring->desc + (rrd_ring->size + offset));
+
+ /* init SMB */
+ adapter->smb.dma = adapter->cmb.dma + sizeof(struct coals_msg_block);
+ offset = (adapter->smb.dma & 0x7) ? (8 - (adapter->smb.dma & 0x7)) : 0;
+ adapter->smb.dma += offset;
+ adapter->smb.smb = (struct stats_msg_block *)
+ ((u8 *) adapter->cmb.cmb +
+ (sizeof(struct coals_msg_block) + offset));
+
+ return 0;
+
+err_nomem:
+ kfree(tpd_ring->buffer_info);
+ return -ENOMEM;
+}
+
+static void atl1_init_ring_ptrs(struct atl1_adapter *adapter)
+{
+ struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
+ struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring;
+ struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring;
+
+ atomic_set(&tpd_ring->next_to_use, 0);
+ atomic_set(&tpd_ring->next_to_clean, 0);
+
+ rfd_ring->next_to_clean = 0;
+ atomic_set(&rfd_ring->next_to_use, 0);
+
+ rrd_ring->next_to_use = 0;
+ atomic_set(&rrd_ring->next_to_clean, 0);
+}
+
+/*
+ * atl1_clean_rx_ring - Free RFD Buffers
+ * @adapter: board private structure
+ */
+static void atl1_clean_rx_ring(struct atl1_adapter *adapter)
+{
+ struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring;
+ struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring;
+ struct atl1_buffer *buffer_info;
+ struct pci_dev *pdev = adapter->pdev;
+ unsigned long size;
+ unsigned int i;
+
+ /* Free all the Rx ring sk_buffs */
+ for (i = 0; i < rfd_ring->count; i++) {
+ buffer_info = &rfd_ring->buffer_info[i];
+ if (buffer_info->dma) {
+ pci_unmap_page(pdev, buffer_info->dma,
+ buffer_info->length, PCI_DMA_FROMDEVICE);
+ buffer_info->dma = 0;
+ }
+ if (buffer_info->skb) {
+ dev_kfree_skb(buffer_info->skb);
+ buffer_info->skb = NULL;
+ }
+ }
+
+ size = sizeof(struct atl1_buffer) * rfd_ring->count;
+ memset(rfd_ring->buffer_info, 0, size);
+
+ /* Zero out the descriptor ring */
+ memset(rfd_ring->desc, 0, rfd_ring->size);
+
+ rfd_ring->next_to_clean = 0;
+ atomic_set(&rfd_ring->next_to_use, 0);
+
+ rrd_ring->next_to_use = 0;
+ atomic_set(&rrd_ring->next_to_clean, 0);
+}
+
+/*
+ * atl1_clean_tx_ring - Free Tx Buffers
+ * @adapter: board private structure
+ */
+static void atl1_clean_tx_ring(struct atl1_adapter *adapter)
+{
+ struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
+ struct atl1_buffer *buffer_info;
+ struct pci_dev *pdev = adapter->pdev;
+ unsigned long size;
+ unsigned int i;
+
+ /* Free all the Tx ring sk_buffs */
+ for (i = 0; i < tpd_ring->count; i++) {
+ buffer_info = &tpd_ring->buffer_info[i];
+ if (buffer_info->dma) {
+ pci_unmap_page(pdev, buffer_info->dma,
+ buffer_info->length, PCI_DMA_TODEVICE);
+ buffer_info->dma = 0;
+ }
+ }
+
+ for (i = 0; i < tpd_ring->count; i++) {
+ buffer_info = &tpd_ring->buffer_info[i];
+ if (buffer_info->skb) {
+ dev_kfree_skb_any(buffer_info->skb);
+ buffer_info->skb = NULL;
+ }
+ }
+
+ size = sizeof(struct atl1_buffer) * tpd_ring->count;
+ memset(tpd_ring->buffer_info, 0, size);
+
+ /* Zero out the descriptor ring */
+ memset(tpd_ring->desc, 0, tpd_ring->size);
+
+ atomic_set(&tpd_ring->next_to_use, 0);
+ atomic_set(&tpd_ring->next_to_clean, 0);
+}
+
+/*
+ * atl1_free_ring_resources - Free Tx / RX descriptor Resources
+ * @adapter: board private structure
+ *
+ * Free all transmit software resources
+ */
+static void atl1_free_ring_resources(struct atl1_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
+ struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring;
+ struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring;
+ struct atl1_ring_header *ring_header = &adapter->ring_header;
+
+ atl1_clean_tx_ring(adapter);
+ atl1_clean_rx_ring(adapter);
+
+ kfree(tpd_ring->buffer_info);
+ pci_free_consistent(pdev, ring_header->size, ring_header->desc,
+ ring_header->dma);
+
+ tpd_ring->buffer_info = NULL;
+ tpd_ring->desc = NULL;
+ tpd_ring->dma = 0;
+
+ rfd_ring->buffer_info = NULL;
+ rfd_ring->desc = NULL;
+ rfd_ring->dma = 0;
+
+ rrd_ring->desc = NULL;
+ rrd_ring->dma = 0;
+
+ adapter->cmb.dma = 0;
+ adapter->cmb.cmb = NULL;
+
+ adapter->smb.dma = 0;
+ adapter->smb.smb = NULL;
+}
+
+static void atl1_setup_mac_ctrl(struct atl1_adapter *adapter)
+{
+ u32 value;
+ struct atl1_hw *hw = &adapter->hw;
+ struct net_device *netdev = adapter->netdev;
+ /* Config MAC CTRL Register */
+ value = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN;
+ /* duplex */
+ if (FULL_DUPLEX == adapter->link_duplex)
+ value |= MAC_CTRL_DUPLX;
+ /* speed */
+ value |= ((u32) ((SPEED_1000 == adapter->link_speed) ?
+ MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100) <<
+ MAC_CTRL_SPEED_SHIFT);
+ /* flow control */
+ value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW);
+ /* PAD & CRC */
+ value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD);
+ /* preamble length */
+ value |= (((u32) adapter->hw.preamble_len
+ & MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT);
+ /* vlan */
+ __atlx_vlan_mode(netdev->features, &value);
+ /* rx checksum
+ if (adapter->rx_csum)
+ value |= MAC_CTRL_RX_CHKSUM_EN;
+ */
+ /* filter mode */
+ value |= MAC_CTRL_BC_EN;
+ if (netdev->flags & IFF_PROMISC)
+ value |= MAC_CTRL_PROMIS_EN;
+ else if (netdev->flags & IFF_ALLMULTI)
+ value |= MAC_CTRL_MC_ALL_EN;
+ /* value |= MAC_CTRL_LOOPBACK; */
+ iowrite32(value, hw->hw_addr + REG_MAC_CTRL);
+}
+
+static u32 atl1_check_link(struct atl1_adapter *adapter)
+{
+ struct atl1_hw *hw = &adapter->hw;
+ struct net_device *netdev = adapter->netdev;
+ u32 ret_val;
+ u16 speed, duplex, phy_data;
+ int reconfig = 0;
+
+ /* MII_BMSR must read twice */
+ atl1_read_phy_reg(hw, MII_BMSR, &phy_data);
+ atl1_read_phy_reg(hw, MII_BMSR, &phy_data);
+ if (!(phy_data & BMSR_LSTATUS)) {
+ /* link down */
+ if (netif_carrier_ok(netdev)) {
+ /* old link state: Up */
+ if (netif_msg_link(adapter))
+ dev_info(&adapter->pdev->dev, "link is down\n");
+ adapter->link_speed = SPEED_0;
+ netif_carrier_off(netdev);
+ }
+ return 0;
+ }
+
+ /* Link Up */
+ ret_val = atl1_get_speed_and_duplex(hw, &speed, &duplex);
+ if (ret_val)
+ return ret_val;
+
+ switch (hw->media_type) {
+ case MEDIA_TYPE_1000M_FULL:
+ if (speed != SPEED_1000 || duplex != FULL_DUPLEX)
+ reconfig = 1;
+ break;
+ case MEDIA_TYPE_100M_FULL:
+ if (speed != SPEED_100 || duplex != FULL_DUPLEX)
+ reconfig = 1;
+ break;
+ case MEDIA_TYPE_100M_HALF:
+ if (speed != SPEED_100 || duplex != HALF_DUPLEX)
+ reconfig = 1;
+ break;
+ case MEDIA_TYPE_10M_FULL:
+ if (speed != SPEED_10 || duplex != FULL_DUPLEX)
+ reconfig = 1;
+ break;
+ case MEDIA_TYPE_10M_HALF:
+ if (speed != SPEED_10 || duplex != HALF_DUPLEX)
+ reconfig = 1;
+ break;
+ }
+
+ /* link result is our setting */
+ if (!reconfig) {
+ if (adapter->link_speed != speed ||
+ adapter->link_duplex != duplex) {
+ adapter->link_speed = speed;
+ adapter->link_duplex = duplex;
+ atl1_setup_mac_ctrl(adapter);
+ if (netif_msg_link(adapter))
+ dev_info(&adapter->pdev->dev,
+ "%s link is up %d Mbps %s\n",
+ netdev->name, adapter->link_speed,
+ adapter->link_duplex == FULL_DUPLEX ?
+ "full duplex" : "half duplex");
+ }
+ if (!netif_carrier_ok(netdev)) {
+ /* Link down -> Up */
+ netif_carrier_on(netdev);
+ }
+ return 0;
+ }
+
+ /* change original link status */
+ if (netif_carrier_ok(netdev)) {
+ adapter->link_speed = SPEED_0;
+ netif_carrier_off(netdev);
+ netif_stop_queue(netdev);
+ }
+
+ if (hw->media_type != MEDIA_TYPE_AUTO_SENSOR &&
+ hw->media_type != MEDIA_TYPE_1000M_FULL) {
+ switch (hw->media_type) {
+ case MEDIA_TYPE_100M_FULL:
+ phy_data = MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 |
+ MII_CR_RESET;
+ break;
+ case MEDIA_TYPE_100M_HALF:
+ phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
+ break;
+ case MEDIA_TYPE_10M_FULL:
+ phy_data =
+ MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET;
+ break;
+ default:
+ /* MEDIA_TYPE_10M_HALF: */
+ phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
+ break;
+ }
+ atl1_write_phy_reg(hw, MII_BMCR, phy_data);
+ return 0;
+ }
+
+ /* auto-neg, insert timer to re-config phy */
+ if (!adapter->phy_timer_pending) {
+ adapter->phy_timer_pending = true;
+ mod_timer(&adapter->phy_config_timer,
+ round_jiffies(jiffies + 3 * HZ));
+ }
+
+ return 0;
+}
+
+static void set_flow_ctrl_old(struct atl1_adapter *adapter)
+{
+ u32 hi, lo, value;
+
+ /* RFD Flow Control */
+ value = adapter->rfd_ring.count;
+ hi = value / 16;
+ if (hi < 2)
+ hi = 2;
+ lo = value * 7 / 8;
+
+ value = ((hi & RXQ_RXF_PAUSE_TH_HI_MASK) << RXQ_RXF_PAUSE_TH_HI_SHIFT) |
+ ((lo & RXQ_RXF_PAUSE_TH_LO_MASK) << RXQ_RXF_PAUSE_TH_LO_SHIFT);
+ iowrite32(value, adapter->hw.hw_addr + REG_RXQ_RXF_PAUSE_THRESH);
+
+ /* RRD Flow Control */
+ value = adapter->rrd_ring.count;
+ lo = value / 16;
+ hi = value * 7 / 8;
+ if (lo < 2)
+ lo = 2;
+ value = ((hi & RXQ_RRD_PAUSE_TH_HI_MASK) << RXQ_RRD_PAUSE_TH_HI_SHIFT) |
+ ((lo & RXQ_RRD_PAUSE_TH_LO_MASK) << RXQ_RRD_PAUSE_TH_LO_SHIFT);
+ iowrite32(value, adapter->hw.hw_addr + REG_RXQ_RRD_PAUSE_THRESH);
+}
+
+static void set_flow_ctrl_new(struct atl1_hw *hw)
+{
+ u32 hi, lo, value;
+
+ /* RXF Flow Control */
+ value = ioread32(hw->hw_addr + REG_SRAM_RXF_LEN);
+ lo = value / 16;
+ if (lo < 192)
+ lo = 192;
+ hi = value * 7 / 8;
+ if (hi < lo)
+ hi = lo + 16;
+ value = ((hi & RXQ_RXF_PAUSE_TH_HI_MASK) << RXQ_RXF_PAUSE_TH_HI_SHIFT) |
+ ((lo & RXQ_RXF_PAUSE_TH_LO_MASK) << RXQ_RXF_PAUSE_TH_LO_SHIFT);
+ iowrite32(value, hw->hw_addr + REG_RXQ_RXF_PAUSE_THRESH);
+
+ /* RRD Flow Control */
+ value = ioread32(hw->hw_addr + REG_SRAM_RRD_LEN);
+ lo = value / 8;
+ hi = value * 7 / 8;
+ if (lo < 2)
+ lo = 2;
+ if (hi < lo)
+ hi = lo + 3;
+ value = ((hi & RXQ_RRD_PAUSE_TH_HI_MASK) << RXQ_RRD_PAUSE_TH_HI_SHIFT) |
+ ((lo & RXQ_RRD_PAUSE_TH_LO_MASK) << RXQ_RRD_PAUSE_TH_LO_SHIFT);
+ iowrite32(value, hw->hw_addr + REG_RXQ_RRD_PAUSE_THRESH);
+}
+
+/*
+ * atl1_configure - Configure Transmit&Receive Unit after Reset
+ * @adapter: board private structure
+ *
+ * Configure the Tx /Rx unit of the MAC after a reset.
+ */
+static u32 atl1_configure(struct atl1_adapter *adapter)
+{
+ struct atl1_hw *hw = &adapter->hw;
+ u32 value;
+
+ /* clear interrupt status */
+ iowrite32(0xffffffff, adapter->hw.hw_addr + REG_ISR);
+
+ /* set MAC Address */
+ value = (((u32) hw->mac_addr[2]) << 24) |
+ (((u32) hw->mac_addr[3]) << 16) |
+ (((u32) hw->mac_addr[4]) << 8) |
+ (((u32) hw->mac_addr[5]));
+ iowrite32(value, hw->hw_addr + REG_MAC_STA_ADDR);
+ value = (((u32) hw->mac_addr[0]) << 8) | (((u32) hw->mac_addr[1]));
+ iowrite32(value, hw->hw_addr + (REG_MAC_STA_ADDR + 4));
+
+ /* tx / rx ring */
+
+ /* HI base address */
+ iowrite32((u32) ((adapter->tpd_ring.dma & 0xffffffff00000000ULL) >> 32),
+ hw->hw_addr + REG_DESC_BASE_ADDR_HI);
+ /* LO base address */
+ iowrite32((u32) (adapter->rfd_ring.dma & 0x00000000ffffffffULL),
+ hw->hw_addr + REG_DESC_RFD_ADDR_LO);
+ iowrite32((u32) (adapter->rrd_ring.dma & 0x00000000ffffffffULL),
+ hw->hw_addr + REG_DESC_RRD_ADDR_LO);
+ iowrite32((u32) (adapter->tpd_ring.dma & 0x00000000ffffffffULL),
+ hw->hw_addr + REG_DESC_TPD_ADDR_LO);
+ iowrite32((u32) (adapter->cmb.dma & 0x00000000ffffffffULL),
+ hw->hw_addr + REG_DESC_CMB_ADDR_LO);
+ iowrite32((u32) (adapter->smb.dma & 0x00000000ffffffffULL),
+ hw->hw_addr + REG_DESC_SMB_ADDR_LO);
+
+ /* element count */
+ value = adapter->rrd_ring.count;
+ value <<= 16;
+ value += adapter->rfd_ring.count;
+ iowrite32(value, hw->hw_addr + REG_DESC_RFD_RRD_RING_SIZE);
+ iowrite32(adapter->tpd_ring.count, hw->hw_addr +
+ REG_DESC_TPD_RING_SIZE);
+
+ /* Load Ptr */
+ iowrite32(1, hw->hw_addr + REG_LOAD_PTR);
+
+ /* config Mailbox */
+ value = ((atomic_read(&adapter->tpd_ring.next_to_use)
+ & MB_TPD_PROD_INDX_MASK) << MB_TPD_PROD_INDX_SHIFT) |
+ ((atomic_read(&adapter->rrd_ring.next_to_clean)
+ & MB_RRD_CONS_INDX_MASK) << MB_RRD_CONS_INDX_SHIFT) |
+ ((atomic_read(&adapter->rfd_ring.next_to_use)
+ & MB_RFD_PROD_INDX_MASK) << MB_RFD_PROD_INDX_SHIFT);
+ iowrite32(value, hw->hw_addr + REG_MAILBOX);
+
+ /* config IPG/IFG */
+ value = (((u32) hw->ipgt & MAC_IPG_IFG_IPGT_MASK)
+ << MAC_IPG_IFG_IPGT_SHIFT) |
+ (((u32) hw->min_ifg & MAC_IPG_IFG_MIFG_MASK)
+ << MAC_IPG_IFG_MIFG_SHIFT) |
+ (((u32) hw->ipgr1 & MAC_IPG_IFG_IPGR1_MASK)
+ << MAC_IPG_IFG_IPGR1_SHIFT) |
+ (((u32) hw->ipgr2 & MAC_IPG_IFG_IPGR2_MASK)
+ << MAC_IPG_IFG_IPGR2_SHIFT);
+ iowrite32(value, hw->hw_addr + REG_MAC_IPG_IFG);
+
+ /* config Half-Duplex Control */
+ value = ((u32) hw->lcol & MAC_HALF_DUPLX_CTRL_LCOL_MASK) |
+ (((u32) hw->max_retry & MAC_HALF_DUPLX_CTRL_RETRY_MASK)
+ << MAC_HALF_DUPLX_CTRL_RETRY_SHIFT) |
+ MAC_HALF_DUPLX_CTRL_EXC_DEF_EN |
+ (0xa << MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT) |
+ (((u32) hw->jam_ipg & MAC_HALF_DUPLX_CTRL_JAMIPG_MASK)
+ << MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT);
+ iowrite32(value, hw->hw_addr + REG_MAC_HALF_DUPLX_CTRL);
+
+ /* set Interrupt Moderator Timer */
+ iowrite16(adapter->imt, hw->hw_addr + REG_IRQ_MODU_TIMER_INIT);
+ iowrite32(MASTER_CTRL_ITIMER_EN, hw->hw_addr + REG_MASTER_CTRL);
+
+ /* set Interrupt Clear Timer */
+ iowrite16(adapter->ict, hw->hw_addr + REG_CMBDISDMA_TIMER);
+
+ /* set max frame size hw will accept */
+ iowrite32(hw->max_frame_size, hw->hw_addr + REG_MTU);
+
+ /* jumbo size & rrd retirement timer */
+ value = (((u32) hw->rx_jumbo_th & RXQ_JMBOSZ_TH_MASK)
+ << RXQ_JMBOSZ_TH_SHIFT) |
+ (((u32) hw->rx_jumbo_lkah & RXQ_JMBO_LKAH_MASK)
+ << RXQ_JMBO_LKAH_SHIFT) |
+ (((u32) hw->rrd_ret_timer & RXQ_RRD_TIMER_MASK)
+ << RXQ_RRD_TIMER_SHIFT);
+ iowrite32(value, hw->hw_addr + REG_RXQ_JMBOSZ_RRDTIM);
+
+ /* Flow Control */
+ switch (hw->dev_rev) {
+ case 0x8001:
+ case 0x9001:
+ case 0x9002:
+ case 0x9003:
+ set_flow_ctrl_old(adapter);
+ break;
+ default:
+ set_flow_ctrl_new(hw);
+ break;
+ }
+
+ /* config TXQ */
+ value = (((u32) hw->tpd_burst & TXQ_CTRL_TPD_BURST_NUM_MASK)
+ << TXQ_CTRL_TPD_BURST_NUM_SHIFT) |
+ (((u32) hw->txf_burst & TXQ_CTRL_TXF_BURST_NUM_MASK)
+ << TXQ_CTRL_TXF_BURST_NUM_SHIFT) |
+ (((u32) hw->tpd_fetch_th & TXQ_CTRL_TPD_FETCH_TH_MASK)
+ << TXQ_CTRL_TPD_FETCH_TH_SHIFT) | TXQ_CTRL_ENH_MODE |
+ TXQ_CTRL_EN;
+ iowrite32(value, hw->hw_addr + REG_TXQ_CTRL);
+
+ /* min tpd fetch gap & tx jumbo packet size threshold for taskoffload */
+ value = (((u32) hw->tx_jumbo_task_th & TX_JUMBO_TASK_TH_MASK)
+ << TX_JUMBO_TASK_TH_SHIFT) |
+ (((u32) hw->tpd_fetch_gap & TX_TPD_MIN_IPG_MASK)
+ << TX_TPD_MIN_IPG_SHIFT);
+ iowrite32(value, hw->hw_addr + REG_TX_JUMBO_TASK_TH_TPD_IPG);
+
+ /* config RXQ */
+ value = (((u32) hw->rfd_burst & RXQ_CTRL_RFD_BURST_NUM_MASK)
+ << RXQ_CTRL_RFD_BURST_NUM_SHIFT) |
+ (((u32) hw->rrd_burst & RXQ_CTRL_RRD_BURST_THRESH_MASK)
+ << RXQ_CTRL_RRD_BURST_THRESH_SHIFT) |
+ (((u32) hw->rfd_fetch_gap & RXQ_CTRL_RFD_PREF_MIN_IPG_MASK)
+ << RXQ_CTRL_RFD_PREF_MIN_IPG_SHIFT) | RXQ_CTRL_CUT_THRU_EN |
+ RXQ_CTRL_EN;
+ iowrite32(value, hw->hw_addr + REG_RXQ_CTRL);
+
+ /* config DMA Engine */
+ value = ((((u32) hw->dmar_block) & DMA_CTRL_DMAR_BURST_LEN_MASK)
+ << DMA_CTRL_DMAR_BURST_LEN_SHIFT) |
+ ((((u32) hw->dmaw_block) & DMA_CTRL_DMAW_BURST_LEN_MASK)
+ << DMA_CTRL_DMAW_BURST_LEN_SHIFT) | DMA_CTRL_DMAR_EN |
+ DMA_CTRL_DMAW_EN;
+ value |= (u32) hw->dma_ord;
+ if (atl1_rcb_128 == hw->rcb_value)
+ value |= DMA_CTRL_RCB_VALUE;
+ iowrite32(value, hw->hw_addr + REG_DMA_CTRL);
+
+ /* config CMB / SMB */
+ value = (hw->cmb_tpd > adapter->tpd_ring.count) ?
+ hw->cmb_tpd : adapter->tpd_ring.count;
+ value <<= 16;
+ value |= hw->cmb_rrd;
+ iowrite32(value, hw->hw_addr + REG_CMB_WRITE_TH);
+ value = hw->cmb_rx_timer | ((u32) hw->cmb_tx_timer << 16);
+ iowrite32(value, hw->hw_addr + REG_CMB_WRITE_TIMER);
+ iowrite32(hw->smb_timer, hw->hw_addr + REG_SMB_TIMER);
+
+ /* --- enable CMB / SMB */
+ value = CSMB_CTRL_CMB_EN | CSMB_CTRL_SMB_EN;
+ iowrite32(value, hw->hw_addr + REG_CSMB_CTRL);
+
+ value = ioread32(adapter->hw.hw_addr + REG_ISR);
+ if (unlikely((value & ISR_PHY_LINKDOWN) != 0))
+ value = 1; /* config failed */
+ else
+ value = 0;
+
+ /* clear all interrupt status */
+ iowrite32(0x3fffffff, adapter->hw.hw_addr + REG_ISR);
+ iowrite32(0, adapter->hw.hw_addr + REG_ISR);
+ return value;
+}
+
+/*
+ * atl1_pcie_patch - Patch for PCIE module
+ */
+static void atl1_pcie_patch(struct atl1_adapter *adapter)
+{
+ u32 value;
+
+ /* much vendor magic here */
+ value = 0x6500;
+ iowrite32(value, adapter->hw.hw_addr + 0x12FC);
+ /* pcie flow control mode change */
+ value = ioread32(adapter->hw.hw_addr + 0x1008);
+ value |= 0x8000;
+ iowrite32(value, adapter->hw.hw_addr + 0x1008);
+}
+
+/*
+ * When ACPI resume on some VIA MotherBoard, the Interrupt Disable bit/0x400
+ * on PCI Command register is disable.
+ * The function enable this bit.
+ * Brackett, 2006/03/15
+ */
+static void atl1_via_workaround(struct atl1_adapter *adapter)
+{
+ unsigned long value;
+
+ value = ioread16(adapter->hw.hw_addr + PCI_COMMAND);
+ if (value & PCI_COMMAND_INTX_DISABLE)
+ value &= ~PCI_COMMAND_INTX_DISABLE;
+ iowrite32(value, adapter->hw.hw_addr + PCI_COMMAND);
+}
+
+static void atl1_inc_smb(struct atl1_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ struct stats_msg_block *smb = adapter->smb.smb;
+
+ /* Fill out the OS statistics structure */
+ adapter->soft_stats.rx_packets += smb->rx_ok;
+ adapter->soft_stats.tx_packets += smb->tx_ok;
+ adapter->soft_stats.rx_bytes += smb->rx_byte_cnt;
+ adapter->soft_stats.tx_bytes += smb->tx_byte_cnt;
+ adapter->soft_stats.multicast += smb->rx_mcast;
+ adapter->soft_stats.collisions += (smb->tx_1_col + smb->tx_2_col * 2 +
+ smb->tx_late_col + smb->tx_abort_col * adapter->hw.max_retry);
+
+ /* Rx Errors */
+ adapter->soft_stats.rx_errors += (smb->rx_frag + smb->rx_fcs_err +
+ smb->rx_len_err + smb->rx_sz_ov + smb->rx_rxf_ov +
+ smb->rx_rrd_ov + smb->rx_align_err);
+ adapter->soft_stats.rx_fifo_errors += smb->rx_rxf_ov;
+ adapter->soft_stats.rx_length_errors += smb->rx_len_err;
+ adapter->soft_stats.rx_crc_errors += smb->rx_fcs_err;
+ adapter->soft_stats.rx_frame_errors += smb->rx_align_err;
+ adapter->soft_stats.rx_missed_errors += (smb->rx_rrd_ov +
+ smb->rx_rxf_ov);
+
+ adapter->soft_stats.rx_pause += smb->rx_pause;
+ adapter->soft_stats.rx_rrd_ov += smb->rx_rrd_ov;
+ adapter->soft_stats.rx_trunc += smb->rx_sz_ov;
+
+ /* Tx Errors */
+ adapter->soft_stats.tx_errors += (smb->tx_late_col +
+ smb->tx_abort_col + smb->tx_underrun + smb->tx_trunc);
+ adapter->soft_stats.tx_fifo_errors += smb->tx_underrun;
+ adapter->soft_stats.tx_aborted_errors += smb->tx_abort_col;
+ adapter->soft_stats.tx_window_errors += smb->tx_late_col;
+
+ adapter->soft_stats.excecol += smb->tx_abort_col;
+ adapter->soft_stats.deffer += smb->tx_defer;
+ adapter->soft_stats.scc += smb->tx_1_col;
+ adapter->soft_stats.mcc += smb->tx_2_col;
+ adapter->soft_stats.latecol += smb->tx_late_col;
+ adapter->soft_stats.tx_underun += smb->tx_underrun;
+ adapter->soft_stats.tx_trunc += smb->tx_trunc;
+ adapter->soft_stats.tx_pause += smb->tx_pause;
+
+ netdev->stats.rx_packets = adapter->soft_stats.rx_packets;
+ netdev->stats.tx_packets = adapter->soft_stats.tx_packets;
+ netdev->stats.rx_bytes = adapter->soft_stats.rx_bytes;
+ netdev->stats.tx_bytes = adapter->soft_stats.tx_bytes;
+ netdev->stats.multicast = adapter->soft_stats.multicast;
+ netdev->stats.collisions = adapter->soft_stats.collisions;
+ netdev->stats.rx_errors = adapter->soft_stats.rx_errors;
+ netdev->stats.rx_over_errors =
+ adapter->soft_stats.rx_missed_errors;
+ netdev->stats.rx_length_errors =
+ adapter->soft_stats.rx_length_errors;
+ netdev->stats.rx_crc_errors = adapter->soft_stats.rx_crc_errors;
+ netdev->stats.rx_frame_errors =
+ adapter->soft_stats.rx_frame_errors;
+ netdev->stats.rx_fifo_errors = adapter->soft_stats.rx_fifo_errors;
+ netdev->stats.rx_missed_errors =
+ adapter->soft_stats.rx_missed_errors;
+ netdev->stats.tx_errors = adapter->soft_stats.tx_errors;
+ netdev->stats.tx_fifo_errors = adapter->soft_stats.tx_fifo_errors;
+ netdev->stats.tx_aborted_errors =
+ adapter->soft_stats.tx_aborted_errors;
+ netdev->stats.tx_window_errors =
+ adapter->soft_stats.tx_window_errors;
+ netdev->stats.tx_carrier_errors =
+ adapter->soft_stats.tx_carrier_errors;
+}
+
+static void atl1_update_mailbox(struct atl1_adapter *adapter)
+{
+ unsigned long flags;
+ u32 tpd_next_to_use;
+ u32 rfd_next_to_use;
+ u32 rrd_next_to_clean;
+ u32 value;
+
+ spin_lock_irqsave(&adapter->mb_lock, flags);
+
+ tpd_next_to_use = atomic_read(&adapter->tpd_ring.next_to_use);
+ rfd_next_to_use = atomic_read(&adapter->rfd_ring.next_to_use);
+ rrd_next_to_clean = atomic_read(&adapter->rrd_ring.next_to_clean);
+
+ value = ((rfd_next_to_use & MB_RFD_PROD_INDX_MASK) <<
+ MB_RFD_PROD_INDX_SHIFT) |
+ ((rrd_next_to_clean & MB_RRD_CONS_INDX_MASK) <<
+ MB_RRD_CONS_INDX_SHIFT) |
+ ((tpd_next_to_use & MB_TPD_PROD_INDX_MASK) <<
+ MB_TPD_PROD_INDX_SHIFT);
+ iowrite32(value, adapter->hw.hw_addr + REG_MAILBOX);
+
+ spin_unlock_irqrestore(&adapter->mb_lock, flags);
+}
+
+static void atl1_clean_alloc_flag(struct atl1_adapter *adapter,
+ struct rx_return_desc *rrd, u16 offset)
+{
+ struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring;
+
+ while (rfd_ring->next_to_clean != (rrd->buf_indx + offset)) {
+ rfd_ring->buffer_info[rfd_ring->next_to_clean].alloced = 0;
+ if (++rfd_ring->next_to_clean == rfd_ring->count) {
+ rfd_ring->next_to_clean = 0;
+ }
+ }
+}
+
+static void atl1_update_rfd_index(struct atl1_adapter *adapter,
+ struct rx_return_desc *rrd)
+{
+ u16 num_buf;
+
+ num_buf = (rrd->xsz.xsum_sz.pkt_size + adapter->rx_buffer_len - 1) /
+ adapter->rx_buffer_len;
+ if (rrd->num_buf == num_buf)
+ /* clean alloc flag for bad rrd */
+ atl1_clean_alloc_flag(adapter, rrd, num_buf);
+}
+
+static void atl1_rx_checksum(struct atl1_adapter *adapter,
+ struct rx_return_desc *rrd, struct sk_buff *skb)
+{
+ struct pci_dev *pdev = adapter->pdev;
+
+ /*
+ * The L1 hardware contains a bug that erroneously sets the
+ * PACKET_FLAG_ERR and ERR_FLAG_L4_CHKSUM bits whenever a
+ * fragmented IP packet is received, even though the packet
+ * is perfectly valid and its checksum is correct. There's
+ * no way to distinguish between one of these good packets
+ * and a packet that actually contains a TCP/UDP checksum
+ * error, so all we can do is allow it to be handed up to
+ * the higher layers and let it be sorted out there.
+ */
+
+ skb_checksum_none_assert(skb);
+
+ if (unlikely(rrd->pkt_flg & PACKET_FLAG_ERR)) {
+ if (rrd->err_flg & (ERR_FLAG_CRC | ERR_FLAG_TRUNC |
+ ERR_FLAG_CODE | ERR_FLAG_OV)) {
+ adapter->hw_csum_err++;
+ if (netif_msg_rx_err(adapter))
+ dev_printk(KERN_DEBUG, &pdev->dev,
+ "rx checksum error\n");
+ return;
+ }
+ }
+
+ /* not IPv4 */
+ if (!(rrd->pkt_flg & PACKET_FLAG_IPV4))
+ /* checksum is invalid, but it's not an IPv4 pkt, so ok */
+ return;
+
+ /* IPv4 packet */
+ if (likely(!(rrd->err_flg &
+ (ERR_FLAG_IP_CHKSUM | ERR_FLAG_L4_CHKSUM)))) {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ adapter->hw_csum_good++;
+ return;
+ }
+}
+
+/*
+ * atl1_alloc_rx_buffers - Replace used receive buffers
+ * @adapter: address of board private structure
+ */
+static u16 atl1_alloc_rx_buffers(struct atl1_adapter *adapter)
+{
+ struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring;
+ struct pci_dev *pdev = adapter->pdev;
+ struct page *page;
+ unsigned long offset;
+ struct atl1_buffer *buffer_info, *next_info;
+ struct sk_buff *skb;
+ u16 num_alloc = 0;
+ u16 rfd_next_to_use, next_next;
+ struct rx_free_desc *rfd_desc;
+
+ next_next = rfd_next_to_use = atomic_read(&rfd_ring->next_to_use);
+ if (++next_next == rfd_ring->count)
+ next_next = 0;
+ buffer_info = &rfd_ring->buffer_info[rfd_next_to_use];
+ next_info = &rfd_ring->buffer_info[next_next];
+
+ while (!buffer_info->alloced && !next_info->alloced) {
+ if (buffer_info->skb) {
+ buffer_info->alloced = 1;
+ goto next;
+ }
+
+ rfd_desc = ATL1_RFD_DESC(rfd_ring, rfd_next_to_use);
+
+ skb = netdev_alloc_skb_ip_align(adapter->netdev,
+ adapter->rx_buffer_len);
+ if (unlikely(!skb)) {
+ /* Better luck next round */
+ adapter->netdev->stats.rx_dropped++;
+ break;
+ }
+
+ buffer_info->alloced = 1;
+ buffer_info->skb = skb;
+ buffer_info->length = (u16) adapter->rx_buffer_len;
+ page = virt_to_page(skb->data);
+ offset = (unsigned long)skb->data & ~PAGE_MASK;
+ buffer_info->dma = pci_map_page(pdev, page, offset,
+ adapter->rx_buffer_len,
+ PCI_DMA_FROMDEVICE);
+ rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
+ rfd_desc->buf_len = cpu_to_le16(adapter->rx_buffer_len);
+ rfd_desc->coalese = 0;
+
+next:
+ rfd_next_to_use = next_next;
+ if (unlikely(++next_next == rfd_ring->count))
+ next_next = 0;
+
+ buffer_info = &rfd_ring->buffer_info[rfd_next_to_use];
+ next_info = &rfd_ring->buffer_info[next_next];
+ num_alloc++;
+ }
+
+ if (num_alloc) {
+ /*
+ * Force memory writes to complete before letting h/w
+ * know there are new descriptors to fetch. (Only
+ * applicable for weak-ordered memory model archs,
+ * such as IA-64).
+ */
+ wmb();
+ atomic_set(&rfd_ring->next_to_use, (int)rfd_next_to_use);
+ }
+ return num_alloc;
+}
+
+static void atl1_intr_rx(struct atl1_adapter *adapter)
+{
+ int i, count;
+ u16 length;
+ u16 rrd_next_to_clean;
+ u32 value;
+ struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring;
+ struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring;
+ struct atl1_buffer *buffer_info;
+ struct rx_return_desc *rrd;
+ struct sk_buff *skb;
+
+ count = 0;
+
+ rrd_next_to_clean = atomic_read(&rrd_ring->next_to_clean);
+
+ while (1) {
+ rrd = ATL1_RRD_DESC(rrd_ring, rrd_next_to_clean);
+ i = 1;
+ if (likely(rrd->xsz.valid)) { /* packet valid */
+chk_rrd:
+ /* check rrd status */
+ if (likely(rrd->num_buf == 1))
+ goto rrd_ok;
+ else if (netif_msg_rx_err(adapter)) {
+ dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+ "unexpected RRD buffer count\n");
+ dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+ "rx_buf_len = %d\n",
+ adapter->rx_buffer_len);
+ dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+ "RRD num_buf = %d\n",
+ rrd->num_buf);
+ dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+ "RRD pkt_len = %d\n",
+ rrd->xsz.xsum_sz.pkt_size);
+ dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+ "RRD pkt_flg = 0x%08X\n",
+ rrd->pkt_flg);
+ dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+ "RRD err_flg = 0x%08X\n",
+ rrd->err_flg);
+ dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+ "RRD vlan_tag = 0x%08X\n",
+ rrd->vlan_tag);
+ }
+
+ /* rrd seems to be bad */
+ if (unlikely(i-- > 0)) {
+ /* rrd may not be DMAed completely */
+ udelay(1);
+ goto chk_rrd;
+ }
+ /* bad rrd */
+ if (netif_msg_rx_err(adapter))
+ dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+ "bad RRD\n");
+ /* see if update RFD index */
+ if (rrd->num_buf > 1)
+ atl1_update_rfd_index(adapter, rrd);
+
+ /* update rrd */
+ rrd->xsz.valid = 0;
+ if (++rrd_next_to_clean == rrd_ring->count)
+ rrd_next_to_clean = 0;
+ count++;
+ continue;
+ } else { /* current rrd still not be updated */
+
+ break;
+ }
+rrd_ok:
+ /* clean alloc flag for bad rrd */
+ atl1_clean_alloc_flag(adapter, rrd, 0);
+
+ buffer_info = &rfd_ring->buffer_info[rrd->buf_indx];
+ if (++rfd_ring->next_to_clean == rfd_ring->count)
+ rfd_ring->next_to_clean = 0;
+
+ /* update rrd next to clean */
+ if (++rrd_next_to_clean == rrd_ring->count)
+ rrd_next_to_clean = 0;
+ count++;
+
+ if (unlikely(rrd->pkt_flg & PACKET_FLAG_ERR)) {
+ if (!(rrd->err_flg &
+ (ERR_FLAG_IP_CHKSUM | ERR_FLAG_L4_CHKSUM
+ | ERR_FLAG_LEN))) {
+ /* packet error, don't need upstream */
+ buffer_info->alloced = 0;
+ rrd->xsz.valid = 0;
+ continue;
+ }
+ }
+
+ /* Good Receive */
+ pci_unmap_page(adapter->pdev, buffer_info->dma,
+ buffer_info->length, PCI_DMA_FROMDEVICE);
+ buffer_info->dma = 0;
+ skb = buffer_info->skb;
+ length = le16_to_cpu(rrd->xsz.xsum_sz.pkt_size);
+
+ skb_put(skb, length - ETH_FCS_LEN);
+
+ /* Receive Checksum Offload */
+ atl1_rx_checksum(adapter, rrd, skb);
+ skb->protocol = eth_type_trans(skb, adapter->netdev);
+
+ if (rrd->pkt_flg & PACKET_FLAG_VLAN_INS) {
+ u16 vlan_tag = (rrd->vlan_tag >> 4) |
+ ((rrd->vlan_tag & 7) << 13) |
+ ((rrd->vlan_tag & 8) << 9);
+
+ __vlan_hwaccel_put_tag(skb, vlan_tag);
+ }
+ netif_rx(skb);
+
+ /* let protocol layer free skb */
+ buffer_info->skb = NULL;
+ buffer_info->alloced = 0;
+ rrd->xsz.valid = 0;
+ }
+
+ atomic_set(&rrd_ring->next_to_clean, rrd_next_to_clean);
+
+ atl1_alloc_rx_buffers(adapter);
+
+ /* update mailbox ? */
+ if (count) {
+ u32 tpd_next_to_use;
+ u32 rfd_next_to_use;
+
+ spin_lock(&adapter->mb_lock);
+
+ tpd_next_to_use = atomic_read(&adapter->tpd_ring.next_to_use);
+ rfd_next_to_use =
+ atomic_read(&adapter->rfd_ring.next_to_use);
+ rrd_next_to_clean =
+ atomic_read(&adapter->rrd_ring.next_to_clean);
+ value = ((rfd_next_to_use & MB_RFD_PROD_INDX_MASK) <<
+ MB_RFD_PROD_INDX_SHIFT) |
+ ((rrd_next_to_clean & MB_RRD_CONS_INDX_MASK) <<
+ MB_RRD_CONS_INDX_SHIFT) |
+ ((tpd_next_to_use & MB_TPD_PROD_INDX_MASK) <<
+ MB_TPD_PROD_INDX_SHIFT);
+ iowrite32(value, adapter->hw.hw_addr + REG_MAILBOX);
+ spin_unlock(&adapter->mb_lock);
+ }
+}
+
+static void atl1_intr_tx(struct atl1_adapter *adapter)
+{
+ struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
+ struct atl1_buffer *buffer_info;
+ u16 sw_tpd_next_to_clean;
+ u16 cmb_tpd_next_to_clean;
+
+ sw_tpd_next_to_clean = atomic_read(&tpd_ring->next_to_clean);
+ cmb_tpd_next_to_clean = le16_to_cpu(adapter->cmb.cmb->tpd_cons_idx);
+
+ while (cmb_tpd_next_to_clean != sw_tpd_next_to_clean) {
+ buffer_info = &tpd_ring->buffer_info[sw_tpd_next_to_clean];
+ if (buffer_info->dma) {
+ pci_unmap_page(adapter->pdev, buffer_info->dma,
+ buffer_info->length, PCI_DMA_TODEVICE);
+ buffer_info->dma = 0;
+ }
+
+ if (buffer_info->skb) {
+ dev_kfree_skb_irq(buffer_info->skb);
+ buffer_info->skb = NULL;
+ }
+
+ if (++sw_tpd_next_to_clean == tpd_ring->count)
+ sw_tpd_next_to_clean = 0;
+ }
+ atomic_set(&tpd_ring->next_to_clean, sw_tpd_next_to_clean);
+
+ if (netif_queue_stopped(adapter->netdev) &&
+ netif_carrier_ok(adapter->netdev))
+ netif_wake_queue(adapter->netdev);
+}
+
+static u16 atl1_tpd_avail(struct atl1_tpd_ring *tpd_ring)
+{
+ u16 next_to_clean = atomic_read(&tpd_ring->next_to_clean);
+ u16 next_to_use = atomic_read(&tpd_ring->next_to_use);
+ return (next_to_clean > next_to_use) ?
+ next_to_clean - next_to_use - 1 :
+ tpd_ring->count + next_to_clean - next_to_use - 1;
+}
+
+static int atl1_tso(struct atl1_adapter *adapter, struct sk_buff *skb,
+ struct tx_packet_desc *ptpd)
+{
+ u8 hdr_len, ip_off;
+ u32 real_len;
+ int err;
+
+ if (skb_shinfo(skb)->gso_size) {
+ if (skb_header_cloned(skb)) {
+ err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+ if (unlikely(err))
+ return -1;
+ }
+
+ if (skb->protocol == htons(ETH_P_IP)) {
+ struct iphdr *iph = ip_hdr(skb);
+
+ real_len = (((unsigned char *)iph - skb->data) +
+ ntohs(iph->tot_len));
+ if (real_len < skb->len)
+ pskb_trim(skb, real_len);
+ hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb));
+ if (skb->len == hdr_len) {
+ iph->check = 0;
+ tcp_hdr(skb)->check =
+ ~csum_tcpudp_magic(iph->saddr,
+ iph->daddr, tcp_hdrlen(skb),
+ IPPROTO_TCP, 0);
+ ptpd->word3 |= (iph->ihl & TPD_IPHL_MASK) <<
+ TPD_IPHL_SHIFT;
+ ptpd->word3 |= ((tcp_hdrlen(skb) >> 2) &
+ TPD_TCPHDRLEN_MASK) <<
+ TPD_TCPHDRLEN_SHIFT;
+ ptpd->word3 |= 1 << TPD_IP_CSUM_SHIFT;
+ ptpd->word3 |= 1 << TPD_TCP_CSUM_SHIFT;
+ return 1;
+ }
+
+ iph->check = 0;
+ tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
+ iph->daddr, 0, IPPROTO_TCP, 0);
+ ip_off = (unsigned char *)iph -
+ (unsigned char *) skb_network_header(skb);
+ if (ip_off == 8) /* 802.3-SNAP frame */
+ ptpd->word3 |= 1 << TPD_ETHTYPE_SHIFT;
+ else if (ip_off != 0)
+ return -2;
+
+ ptpd->word3 |= (iph->ihl & TPD_IPHL_MASK) <<
+ TPD_IPHL_SHIFT;
+ ptpd->word3 |= ((tcp_hdrlen(skb) >> 2) &
+ TPD_TCPHDRLEN_MASK) << TPD_TCPHDRLEN_SHIFT;
+ ptpd->word3 |= (skb_shinfo(skb)->gso_size &
+ TPD_MSS_MASK) << TPD_MSS_SHIFT;
+ ptpd->word3 |= 1 << TPD_SEGMENT_EN_SHIFT;
+ return 3;
+ }
+ }
+ return false;
+}
+
+static int atl1_tx_csum(struct atl1_adapter *adapter, struct sk_buff *skb,
+ struct tx_packet_desc *ptpd)
+{
+ u8 css, cso;
+
+ if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
+ css = skb_checksum_start_offset(skb);
+ cso = css + (u8) skb->csum_offset;
+ if (unlikely(css & 0x1)) {
+ /* L1 hardware requires an even number here */
+ if (netif_msg_tx_err(adapter))
+ dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+ "payload offset not an even number\n");
+ return -1;
+ }
+ ptpd->word3 |= (css & TPD_PLOADOFFSET_MASK) <<
+ TPD_PLOADOFFSET_SHIFT;
+ ptpd->word3 |= (cso & TPD_CCSUMOFFSET_MASK) <<
+ TPD_CCSUMOFFSET_SHIFT;
+ ptpd->word3 |= 1 << TPD_CUST_CSUM_EN_SHIFT;
+ return true;
+ }
+ return 0;
+}
+
+static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb,
+ struct tx_packet_desc *ptpd)
+{
+ struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
+ struct atl1_buffer *buffer_info;
+ u16 buf_len = skb->len;
+ struct page *page;
+ unsigned long offset;
+ unsigned int nr_frags;
+ unsigned int f;
+ int retval;
+ u16 next_to_use;
+ u16 data_len;
+ u8 hdr_len;
+
+ buf_len -= skb->data_len;
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ next_to_use = atomic_read(&tpd_ring->next_to_use);
+ buffer_info = &tpd_ring->buffer_info[next_to_use];
+ BUG_ON(buffer_info->skb);
+ /* put skb in last TPD */
+ buffer_info->skb = NULL;
+
+ retval = (ptpd->word3 >> TPD_SEGMENT_EN_SHIFT) & TPD_SEGMENT_EN_MASK;
+ if (retval) {
+ /* TSO */
+ hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+ buffer_info->length = hdr_len;
+ page = virt_to_page(skb->data);
+ offset = (unsigned long)skb->data & ~PAGE_MASK;
+ buffer_info->dma = pci_map_page(adapter->pdev, page,
+ offset, hdr_len,
+ PCI_DMA_TODEVICE);
+
+ if (++next_to_use == tpd_ring->count)
+ next_to_use = 0;
+
+ if (buf_len > hdr_len) {
+ int i, nseg;
+
+ data_len = buf_len - hdr_len;
+ nseg = (data_len + ATL1_MAX_TX_BUF_LEN - 1) /
+ ATL1_MAX_TX_BUF_LEN;
+ for (i = 0; i < nseg; i++) {
+ buffer_info =
+ &tpd_ring->buffer_info[next_to_use];
+ buffer_info->skb = NULL;
+ buffer_info->length =
+ (ATL1_MAX_TX_BUF_LEN >=
+ data_len) ? ATL1_MAX_TX_BUF_LEN : data_len;
+ data_len -= buffer_info->length;
+ page = virt_to_page(skb->data +
+ (hdr_len + i * ATL1_MAX_TX_BUF_LEN));
+ offset = (unsigned long)(skb->data +
+ (hdr_len + i * ATL1_MAX_TX_BUF_LEN)) &
+ ~PAGE_MASK;
+ buffer_info->dma = pci_map_page(adapter->pdev,
+ page, offset, buffer_info->length,
+ PCI_DMA_TODEVICE);
+ if (++next_to_use == tpd_ring->count)
+ next_to_use = 0;
+ }
+ }
+ } else {
+ /* not TSO */
+ buffer_info->length = buf_len;
+ page = virt_to_page(skb->data);
+ offset = (unsigned long)skb->data & ~PAGE_MASK;
+ buffer_info->dma = pci_map_page(adapter->pdev, page,
+ offset, buf_len, PCI_DMA_TODEVICE);
+ if (++next_to_use == tpd_ring->count)
+ next_to_use = 0;
+ }
+
+ for (f = 0; f < nr_frags; f++) {
+ const struct skb_frag_struct *frag;
+ u16 i, nseg;
+
+ frag = &skb_shinfo(skb)->frags[f];
+ buf_len = skb_frag_size(frag);
+
+ nseg = (buf_len + ATL1_MAX_TX_BUF_LEN - 1) /
+ ATL1_MAX_TX_BUF_LEN;
+ for (i = 0; i < nseg; i++) {
+ buffer_info = &tpd_ring->buffer_info[next_to_use];
+ BUG_ON(buffer_info->skb);
+
+ buffer_info->skb = NULL;
+ buffer_info->length = (buf_len > ATL1_MAX_TX_BUF_LEN) ?
+ ATL1_MAX_TX_BUF_LEN : buf_len;
+ buf_len -= buffer_info->length;
+ buffer_info->dma = skb_frag_dma_map(&adapter->pdev->dev,
+ frag, i * ATL1_MAX_TX_BUF_LEN,
+ buffer_info->length, DMA_TO_DEVICE);
+
+ if (++next_to_use == tpd_ring->count)
+ next_to_use = 0;
+ }
+ }
+
+ /* last tpd's buffer-info */
+ buffer_info->skb = skb;
+}
+
+static void atl1_tx_queue(struct atl1_adapter *adapter, u16 count,
+ struct tx_packet_desc *ptpd)
+{
+ struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
+ struct atl1_buffer *buffer_info;
+ struct tx_packet_desc *tpd;
+ u16 j;
+ u32 val;
+ u16 next_to_use = (u16) atomic_read(&tpd_ring->next_to_use);
+
+ for (j = 0; j < count; j++) {
+ buffer_info = &tpd_ring->buffer_info[next_to_use];
+ tpd = ATL1_TPD_DESC(&adapter->tpd_ring, next_to_use);
+ if (tpd != ptpd)
+ memcpy(tpd, ptpd, sizeof(struct tx_packet_desc));
+ tpd->buffer_addr = cpu_to_le64(buffer_info->dma);
+ tpd->word2 &= ~(TPD_BUFLEN_MASK << TPD_BUFLEN_SHIFT);
+ tpd->word2 |= (cpu_to_le16(buffer_info->length) &
+ TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT;
+
+ /*
+ * if this is the first packet in a TSO chain, set
+ * TPD_HDRFLAG, otherwise, clear it.
+ */
+ val = (tpd->word3 >> TPD_SEGMENT_EN_SHIFT) &
+ TPD_SEGMENT_EN_MASK;
+ if (val) {
+ if (!j)
+ tpd->word3 |= 1 << TPD_HDRFLAG_SHIFT;
+ else
+ tpd->word3 &= ~(1 << TPD_HDRFLAG_SHIFT);
+ }
+
+ if (j == (count - 1))
+ tpd->word3 |= 1 << TPD_EOP_SHIFT;
+
+ if (++next_to_use == tpd_ring->count)
+ next_to_use = 0;
+ }
+ /*
+ * Force memory writes to complete before letting h/w
+ * know there are new descriptors to fetch. (Only
+ * applicable for weak-ordered memory model archs,
+ * such as IA-64).
+ */
+ wmb();
+
+ atomic_set(&tpd_ring->next_to_use, next_to_use);
+}
+
+static netdev_tx_t atl1_xmit_frame(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
+ int len;
+ int tso;
+ int count = 1;
+ int ret_val;
+ struct tx_packet_desc *ptpd;
+ u16 vlan_tag;
+ unsigned int nr_frags = 0;
+ unsigned int mss = 0;
+ unsigned int f;
+ unsigned int proto_hdr_len;
+
+ len = skb_headlen(skb);
+
+ if (unlikely(skb->len <= 0)) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ for (f = 0; f < nr_frags; f++) {
+ unsigned int f_size = skb_frag_size(&skb_shinfo(skb)->frags[f]);
+ count += (f_size + ATL1_MAX_TX_BUF_LEN - 1) /
+ ATL1_MAX_TX_BUF_LEN;
+ }
+
+ mss = skb_shinfo(skb)->gso_size;
+ if (mss) {
+ if (skb->protocol == htons(ETH_P_IP)) {
+ proto_hdr_len = (skb_transport_offset(skb) +
+ tcp_hdrlen(skb));
+ if (unlikely(proto_hdr_len > len)) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+ /* need additional TPD ? */
+ if (proto_hdr_len != len)
+ count += (len - proto_hdr_len +
+ ATL1_MAX_TX_BUF_LEN - 1) /
+ ATL1_MAX_TX_BUF_LEN;
+ }
+ }
+
+ if (atl1_tpd_avail(&adapter->tpd_ring) < count) {
+ /* not enough descriptors */
+ netif_stop_queue(netdev);
+ if (netif_msg_tx_queued(adapter))
+ dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+ "tx busy\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ ptpd = ATL1_TPD_DESC(tpd_ring,
+ (u16) atomic_read(&tpd_ring->next_to_use));
+ memset(ptpd, 0, sizeof(struct tx_packet_desc));
+
+ if (vlan_tx_tag_present(skb)) {
+ vlan_tag = vlan_tx_tag_get(skb);
+ vlan_tag = (vlan_tag << 4) | (vlan_tag >> 13) |
+ ((vlan_tag >> 9) & 0x8);
+ ptpd->word3 |= 1 << TPD_INS_VL_TAG_SHIFT;
+ ptpd->word2 |= (vlan_tag & TPD_VLANTAG_MASK) <<
+ TPD_VLANTAG_SHIFT;
+ }
+
+ tso = atl1_tso(adapter, skb, ptpd);
+ if (tso < 0) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ if (!tso) {
+ ret_val = atl1_tx_csum(adapter, skb, ptpd);
+ if (ret_val < 0) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+ }
+
+ atl1_tx_map(adapter, skb, ptpd);
+ atl1_tx_queue(adapter, count, ptpd);
+ atl1_update_mailbox(adapter);
+ mmiowb();
+ return NETDEV_TX_OK;
+}
+
+/*
+ * atl1_intr - Interrupt Handler
+ * @irq: interrupt number
+ * @data: pointer to a network interface device structure
+ * @pt_regs: CPU registers structure
+ */
+static irqreturn_t atl1_intr(int irq, void *data)
+{
+ struct atl1_adapter *adapter = netdev_priv(data);
+ u32 status;
+ int max_ints = 10;
+
+ status = adapter->cmb.cmb->int_stats;
+ if (!status)
+ return IRQ_NONE;
+
+ do {
+ /* clear CMB interrupt status at once */
+ adapter->cmb.cmb->int_stats = 0;
+
+ if (status & ISR_GPHY) /* clear phy status */
+ atlx_clear_phy_int(adapter);
+
+ /* clear ISR status, and Enable CMB DMA/Disable Interrupt */
+ iowrite32(status | ISR_DIS_INT, adapter->hw.hw_addr + REG_ISR);
+
+ /* check if SMB intr */
+ if (status & ISR_SMB)
+ atl1_inc_smb(adapter);
+
+ /* check if PCIE PHY Link down */
+ if (status & ISR_PHY_LINKDOWN) {
+ if (netif_msg_intr(adapter))
+ dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+ "pcie phy link down %x\n", status);
+ if (netif_running(adapter->netdev)) { /* reset MAC */
+ iowrite32(0, adapter->hw.hw_addr + REG_IMR);
+ schedule_work(&adapter->pcie_dma_to_rst_task);
+ return IRQ_HANDLED;
+ }
+ }
+
+ /* check if DMA read/write error ? */
+ if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) {
+ if (netif_msg_intr(adapter))
+ dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+ "pcie DMA r/w error (status = 0x%x)\n",
+ status);
+ iowrite32(0, adapter->hw.hw_addr + REG_IMR);
+ schedule_work(&adapter->pcie_dma_to_rst_task);
+ return IRQ_HANDLED;
+ }
+
+ /* link event */
+ if (status & ISR_GPHY) {
+ adapter->soft_stats.tx_carrier_errors++;
+ atl1_check_for_link(adapter);
+ }
+
+ /* transmit event */
+ if (status & ISR_CMB_TX)
+ atl1_intr_tx(adapter);
+
+ /* rx exception */
+ if (unlikely(status & (ISR_RXF_OV | ISR_RFD_UNRUN |
+ ISR_RRD_OV | ISR_HOST_RFD_UNRUN |
+ ISR_HOST_RRD_OV | ISR_CMB_RX))) {
+ if (status & (ISR_RXF_OV | ISR_RFD_UNRUN |
+ ISR_RRD_OV | ISR_HOST_RFD_UNRUN |
+ ISR_HOST_RRD_OV))
+ if (netif_msg_intr(adapter))
+ dev_printk(KERN_DEBUG,
+ &adapter->pdev->dev,
+ "rx exception, ISR = 0x%x\n",
+ status);
+ atl1_intr_rx(adapter);
+ }
+
+ if (--max_ints < 0)
+ break;
+
+ } while ((status = adapter->cmb.cmb->int_stats));
+
+ /* re-enable Interrupt */
+ iowrite32(ISR_DIS_SMB | ISR_DIS_DMA, adapter->hw.hw_addr + REG_ISR);
+ return IRQ_HANDLED;
+}
+
+
+/*
+ * atl1_phy_config - Timer Call-back
+ * @data: pointer to netdev cast into an unsigned long
+ */
+static void atl1_phy_config(unsigned long data)
+{
+ struct atl1_adapter *adapter = (struct atl1_adapter *)data;
+ struct atl1_hw *hw = &adapter->hw;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->lock, flags);
+ adapter->phy_timer_pending = false;
+ atl1_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg);
+ atl1_write_phy_reg(hw, MII_ATLX_CR, hw->mii_1000t_ctrl_reg);
+ atl1_write_phy_reg(hw, MII_BMCR, MII_CR_RESET | MII_CR_AUTO_NEG_EN);
+ spin_unlock_irqrestore(&adapter->lock, flags);
+}
+
+/*
+ * Orphaned vendor comment left intact here:
+ * <vendor comment>
+ * If TPD Buffer size equal to 0, PCIE DMAR_TO_INT
+ * will assert. We do soft reset <0x1400=1> according
+ * with the SPEC. BUT, it seemes that PCIE or DMA
+ * state-machine will not be reset. DMAR_TO_INT will
+ * assert again and again.
+ * </vendor comment>
+ */
+
+static int atl1_reset(struct atl1_adapter *adapter)
+{
+ int ret;
+ ret = atl1_reset_hw(&adapter->hw);
+ if (ret)
+ return ret;
+ return atl1_init_hw(&adapter->hw);
+}
+
+static s32 atl1_up(struct atl1_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ int err;
+ int irq_flags = 0;
+
+ /* hardware has been reset, we need to reload some things */
+ atlx_set_multi(netdev);
+ atl1_init_ring_ptrs(adapter);
+ atlx_restore_vlan(adapter);
+ err = atl1_alloc_rx_buffers(adapter);
+ if (unlikely(!err))
+ /* no RX BUFFER allocated */
+ return -ENOMEM;
+
+ if (unlikely(atl1_configure(adapter))) {
+ err = -EIO;
+ goto err_up;
+ }
+
+ err = pci_enable_msi(adapter->pdev);
+ if (err) {
+ if (netif_msg_ifup(adapter))
+ dev_info(&adapter->pdev->dev,
+ "Unable to enable MSI: %d\n", err);
+ irq_flags |= IRQF_SHARED;
+ }
+
+ err = request_irq(adapter->pdev->irq, atl1_intr, irq_flags,
+ netdev->name, netdev);
+ if (unlikely(err))
+ goto err_up;
+
+ atlx_irq_enable(adapter);
+ atl1_check_link(adapter);
+ netif_start_queue(netdev);
+ return 0;
+
+err_up:
+ pci_disable_msi(adapter->pdev);
+ /* free rx_buffers */
+ atl1_clean_rx_ring(adapter);
+ return err;
+}
+
+static void atl1_down(struct atl1_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+
+ netif_stop_queue(netdev);
+ del_timer_sync(&adapter->phy_config_timer);
+ adapter->phy_timer_pending = false;
+
+ atlx_irq_disable(adapter);
+ free_irq(adapter->pdev->irq, netdev);
+ pci_disable_msi(adapter->pdev);
+ atl1_reset_hw(&adapter->hw);
+ adapter->cmb.cmb->int_stats = 0;
+
+ adapter->link_speed = SPEED_0;
+ adapter->link_duplex = -1;
+ netif_carrier_off(netdev);
+
+ atl1_clean_tx_ring(adapter);
+ atl1_clean_rx_ring(adapter);
+}
+
+static void atl1_tx_timeout_task(struct work_struct *work)
+{
+ struct atl1_adapter *adapter =
+ container_of(work, struct atl1_adapter, tx_timeout_task);
+ struct net_device *netdev = adapter->netdev;
+
+ netif_device_detach(netdev);
+ atl1_down(adapter);
+ atl1_up(adapter);
+ netif_device_attach(netdev);
+}
+
+/*
+ * atl1_change_mtu - Change the Maximum Transfer Unit
+ * @netdev: network interface device structure
+ * @new_mtu: new value for maximum frame size
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int atl1_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ int old_mtu = netdev->mtu;
+ int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+
+ if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) ||
+ (max_frame > MAX_JUMBO_FRAME_SIZE)) {
+ if (netif_msg_link(adapter))
+ dev_warn(&adapter->pdev->dev, "invalid MTU setting\n");
+ return -EINVAL;
+ }
+
+ adapter->hw.max_frame_size = max_frame;
+ adapter->hw.tx_jumbo_task_th = (max_frame + 7) >> 3;
+ adapter->rx_buffer_len = (max_frame + 7) & ~7;
+ adapter->hw.rx_jumbo_th = adapter->rx_buffer_len / 8;
+
+ netdev->mtu = new_mtu;
+ if ((old_mtu != new_mtu) && netif_running(netdev)) {
+ atl1_down(adapter);
+ atl1_up(adapter);
+ }
+
+ return 0;
+}
+
+/*
+ * atl1_open - Called when a network interface is made active
+ * @netdev: network interface device structure
+ *
+ * Returns 0 on success, negative value on failure
+ *
+ * The open entry point is called when a network interface is made
+ * active by the system (IFF_UP). At this point all resources needed
+ * for transmit and receive operations are allocated, the interrupt
+ * handler is registered with the OS, the watchdog timer is started,
+ * and the stack is notified that the interface is ready.
+ */
+static int atl1_open(struct net_device *netdev)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ int err;
+
+ netif_carrier_off(netdev);
+
+ /* allocate transmit descriptors */
+ err = atl1_setup_ring_resources(adapter);
+ if (err)
+ return err;
+
+ err = atl1_up(adapter);
+ if (err)
+ goto err_up;
+
+ return 0;
+
+err_up:
+ atl1_reset(adapter);
+ return err;
+}
+
+/*
+ * atl1_close - Disables a network interface
+ * @netdev: network interface device structure
+ *
+ * Returns 0, this is not allowed to fail
+ *
+ * The close entry point is called when an interface is de-activated
+ * by the OS. The hardware is still under the drivers control, but
+ * needs to be disabled. A global MAC reset is issued to stop the
+ * hardware, and all transmit and receive resources are freed.
+ */
+static int atl1_close(struct net_device *netdev)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ atl1_down(adapter);
+ atl1_free_ring_resources(adapter);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int atl1_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_hw *hw = &adapter->hw;
+ u32 ctrl = 0;
+ u32 wufc = adapter->wol;
+ u32 val;
+ u16 speed;
+ u16 duplex;
+
+ netif_device_detach(netdev);
+ if (netif_running(netdev))
+ atl1_down(adapter);
+
+ atl1_read_phy_reg(hw, MII_BMSR, (u16 *) & ctrl);
+ atl1_read_phy_reg(hw, MII_BMSR, (u16 *) & ctrl);
+ val = ctrl & BMSR_LSTATUS;
+ if (val)
+ wufc &= ~ATLX_WUFC_LNKC;
+ if (!wufc)
+ goto disable_wol;
+
+ if (val) {
+ val = atl1_get_speed_and_duplex(hw, &speed, &duplex);
+ if (val) {
+ if (netif_msg_ifdown(adapter))
+ dev_printk(KERN_DEBUG, &pdev->dev,
+ "error getting speed/duplex\n");
+ goto disable_wol;
+ }
+
+ ctrl = 0;
+
+ /* enable magic packet WOL */
+ if (wufc & ATLX_WUFC_MAG)
+ ctrl |= (WOL_MAGIC_EN | WOL_MAGIC_PME_EN);
+ iowrite32(ctrl, hw->hw_addr + REG_WOL_CTRL);
+ ioread32(hw->hw_addr + REG_WOL_CTRL);
+
+ /* configure the mac */
+ ctrl = MAC_CTRL_RX_EN;
+ ctrl |= ((u32)((speed == SPEED_1000) ? MAC_CTRL_SPEED_1000 :
+ MAC_CTRL_SPEED_10_100) << MAC_CTRL_SPEED_SHIFT);
+ if (duplex == FULL_DUPLEX)
+ ctrl |= MAC_CTRL_DUPLX;
+ ctrl |= (((u32)adapter->hw.preamble_len &
+ MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT);
+ __atlx_vlan_mode(netdev->features, &ctrl);
+ if (wufc & ATLX_WUFC_MAG)
+ ctrl |= MAC_CTRL_BC_EN;
+ iowrite32(ctrl, hw->hw_addr + REG_MAC_CTRL);
+ ioread32(hw->hw_addr + REG_MAC_CTRL);
+
+ /* poke the PHY */
+ ctrl = ioread32(hw->hw_addr + REG_PCIE_PHYMISC);
+ ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
+ iowrite32(ctrl, hw->hw_addr + REG_PCIE_PHYMISC);
+ ioread32(hw->hw_addr + REG_PCIE_PHYMISC);
+ } else {
+ ctrl |= (WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN);
+ iowrite32(ctrl, hw->hw_addr + REG_WOL_CTRL);
+ ioread32(hw->hw_addr + REG_WOL_CTRL);
+ iowrite32(0, hw->hw_addr + REG_MAC_CTRL);
+ ioread32(hw->hw_addr + REG_MAC_CTRL);
+ hw->phy_configured = false;
+ }
+
+ return 0;
+
+ disable_wol:
+ iowrite32(0, hw->hw_addr + REG_WOL_CTRL);
+ ioread32(hw->hw_addr + REG_WOL_CTRL);
+ ctrl = ioread32(hw->hw_addr + REG_PCIE_PHYMISC);
+ ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
+ iowrite32(ctrl, hw->hw_addr + REG_PCIE_PHYMISC);
+ ioread32(hw->hw_addr + REG_PCIE_PHYMISC);
+ hw->phy_configured = false;
+
+ return 0;
+}
+
+static int atl1_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+
+ iowrite32(0, adapter->hw.hw_addr + REG_WOL_CTRL);
+
+ atl1_reset_hw(&adapter->hw);
+
+ if (netif_running(netdev)) {
+ adapter->cmb.cmb->int_stats = 0;
+ atl1_up(adapter);
+ }
+ netif_device_attach(netdev);
+
+ return 0;
+}
+
+compat_pci_suspend(atl1_suspend)
+compat_pci_resume(atl1_resume)
+
+static SIMPLE_DEV_PM_OPS(atl1_pm_ops, atl1_suspend, atl1_resume);
+#define ATL1_PM_OPS (&atl1_pm_ops)
+
+#else
+
+static int atl1_suspend(struct device *dev) { return 0; }
+
+#define ATL1_PM_OPS NULL
+#endif
+
+static void atl1_shutdown(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+
+ atl1_suspend(&pdev->dev);
+ pci_wake_from_d3(pdev, adapter->wol);
+ pci_set_power_state(pdev, PCI_D3hot);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void atl1_poll_controller(struct net_device *netdev)
+{
+ disable_irq(netdev->irq);
+ atl1_intr(netdev->irq, netdev);
+ enable_irq(netdev->irq);
+}
+#endif
+
+static const struct net_device_ops atl1_netdev_ops = {
+ .ndo_open = atl1_open,
+ .ndo_stop = atl1_close,
+ .ndo_start_xmit = atl1_xmit_frame,
+ .ndo_set_rx_mode = atlx_set_multi,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = atl1_set_mac,
+ .ndo_change_mtu = atl1_change_mtu,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39))
+ .ndo_fix_features = atlx_fix_features,
+ .ndo_set_features = atlx_set_features,
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */
+ .ndo_do_ioctl = atlx_ioctl,
+ .ndo_tx_timeout = atlx_tx_timeout,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = atl1_poll_controller,
+#endif
+};
+
+/*
+ * atl1_probe - Device Initialization Routine
+ * @pdev: PCI device information struct
+ * @ent: entry in atl1_pci_tbl
+ *
+ * Returns 0 on success, negative on failure
+ *
+ * atl1_probe initializes an adapter identified by a pci_dev structure.
+ * The OS initialization, configuring of the adapter private structure,
+ * and a hardware reset occur.
+ */
+static int __devinit atl1_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *netdev;
+ struct atl1_adapter *adapter;
+ static int cards_found = 0;
+ int err;
+
+ err = pci_enable_device(pdev);
+ if (err)
+ return err;
+
+ /*
+ * The atl1 chip can DMA to 64-bit addresses, but it uses a single
+ * shared register for the high 32 bits, so only a single, aligned,
+ * 4 GB physical address range can be used at a time.
+ *
+ * Supporting 64-bit DMA on this hardware is more trouble than it's
+ * worth. It is far easier to limit to 32-bit DMA than update
+ * various kernel subsystems to support the mechanics required by a
+ * fixed-high-32-bit system.
+ */
+ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (err) {
+ dev_err(&pdev->dev, "no usable DMA configuration\n");
+ goto err_dma;
+ }
+ /*
+ * Mark all PCI regions associated with PCI device
+ * pdev as being reserved by owner atl1_driver_name
+ */
+ err = pci_request_regions(pdev, ATLX_DRIVER_NAME);
+ if (err)
+ goto err_request_regions;
+
+ /*
+ * Enables bus-mastering on the device and calls
+ * pcibios_set_master to do the needed arch specific settings
+ */
+ pci_set_master(pdev);
+
+ netdev = alloc_etherdev(sizeof(struct atl1_adapter));
+ if (!netdev) {
+ err = -ENOMEM;
+ goto err_alloc_etherdev;
+ }
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+
+ pci_set_drvdata(pdev, netdev);
+ adapter = netdev_priv(netdev);
+ adapter->netdev = netdev;
+ adapter->pdev = pdev;
+ adapter->hw.back = adapter;
+ adapter->msg_enable = netif_msg_init(debug, atl1_default_msg);
+
+ adapter->hw.hw_addr = pci_iomap(pdev, 0, 0);
+ if (!adapter->hw.hw_addr) {
+ err = -EIO;
+ goto err_pci_iomap;
+ }
+ /* get device revision number */
+ adapter->hw.dev_rev = ioread16(adapter->hw.hw_addr +
+ (REG_MASTER_CTRL + 2));
+ if (netif_msg_probe(adapter))
+ dev_info(&pdev->dev, "version %s\n", ATLX_DRIVER_VERSION);
+
+ /* set default ring resource counts */
+ adapter->rfd_ring.count = adapter->rrd_ring.count = ATL1_DEFAULT_RFD;
+ adapter->tpd_ring.count = ATL1_DEFAULT_TPD;
+
+ adapter->mii.dev = netdev;
+ adapter->mii.mdio_read = mdio_read;
+ adapter->mii.mdio_write = mdio_write;
+ adapter->mii.phy_id_mask = 0x1f;
+ adapter->mii.reg_num_mask = 0x1f;
+
+ netdev_attach_ops(netdev, &atl1_netdev_ops);
+ netdev->watchdog_timeo = 5 * HZ;
+
+ netdev->ethtool_ops = &atl1_ethtool_ops;
+ adapter->bd_number = cards_found;
+
+ /* setup the private structure */
+ err = atl1_sw_init(adapter);
+ if (err)
+ goto err_common;
+
+ netdev->features = NETIF_F_HW_CSUM;
+ netdev->features |= NETIF_F_SG;
+ netdev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39))
+ netdev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_TSO |
+ NETIF_F_HW_VLAN_RX;
+
+ /* is this valid? see atl1_setup_mac_ctrl() */
+ netdev->features |= NETIF_F_RXCSUM;
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */
+
+ /*
+ * patch for some L1 of old version,
+ * the final version of L1 may not need these
+ * patches
+ */
+ /* atl1_pcie_patch(adapter); */
+
+ /* really reset GPHY core */
+ iowrite16(0, adapter->hw.hw_addr + REG_PHY_ENABLE);
+
+ /*
+ * reset the controller to
+ * put the device in a known good starting state
+ */
+ if (atl1_reset_hw(&adapter->hw)) {
+ err = -EIO;
+ goto err_common;
+ }
+
+ /* copy the MAC address out of the EEPROM */
+ atl1_read_mac_addr(&adapter->hw);
+ memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len);
+
+ if (!is_valid_ether_addr(netdev->dev_addr)) {
+ err = -EIO;
+ goto err_common;
+ }
+
+ atl1_check_options(adapter);
+
+ /* pre-init the MAC, and setup link */
+ err = atl1_init_hw(&adapter->hw);
+ if (err) {
+ err = -EIO;
+ goto err_common;
+ }
+
+ atl1_pcie_patch(adapter);
+ /* assume we have no link for now */
+ netif_carrier_off(netdev);
+
+ setup_timer(&adapter->phy_config_timer, atl1_phy_config,
+ (unsigned long)adapter);
+ adapter->phy_timer_pending = false;
+
+ INIT_WORK(&adapter->tx_timeout_task, atl1_tx_timeout_task);
+
+ INIT_WORK(&adapter->link_chg_task, atlx_link_chg_task);
+
+ INIT_WORK(&adapter->pcie_dma_to_rst_task, atl1_tx_timeout_task);
+
+ err = register_netdev(netdev);
+ if (err)
+ goto err_common;
+
+ cards_found++;
+ atl1_via_workaround(adapter);
+ return 0;
+
+err_common:
+ pci_iounmap(pdev, adapter->hw.hw_addr);
+err_pci_iomap:
+ free_netdev(netdev);
+err_alloc_etherdev:
+ pci_release_regions(pdev);
+err_dma:
+err_request_regions:
+ pci_disable_device(pdev);
+ return err;
+}
+
+/*
+ * atl1_remove - Device Removal Routine
+ * @pdev: PCI device information struct
+ *
+ * atl1_remove is called by the PCI subsystem to alert the driver
+ * that it should release a PCI device. The could be caused by a
+ * Hot-Plug event, or because the driver is going to be removed from
+ * memory.
+ */
+static void __devexit atl1_remove(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl1_adapter *adapter;
+ /* Device not available. Return. */
+ if (!netdev)
+ return;
+
+ adapter = netdev_priv(netdev);
+
+ /*
+ * Some atl1 boards lack persistent storage for their MAC, and get it
+ * from the BIOS during POST. If we've been messing with the MAC
+ * address, we need to save the permanent one.
+ */
+ if (memcmp(adapter->hw.mac_addr, adapter->hw.perm_mac_addr, ETH_ALEN)) {
+ memcpy(adapter->hw.mac_addr, adapter->hw.perm_mac_addr,
+ ETH_ALEN);
+ atl1_set_mac_addr(&adapter->hw);
+ }
+
+ iowrite16(0, adapter->hw.hw_addr + REG_PHY_ENABLE);
+ unregister_netdev(netdev);
+ pci_iounmap(pdev, adapter->hw.hw_addr);
+ pci_release_regions(pdev);
+ free_netdev(netdev);
+ pci_disable_device(pdev);
+}
+
+static struct pci_driver atl1_driver = {
+ .name = ATLX_DRIVER_NAME,
+ .id_table = atl1_pci_tbl,
+ .probe = atl1_probe,
+ .remove = __devexit_p(atl1_remove),
+ .shutdown = atl1_shutdown,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29))
+ .driver.pm = ATL1_PM_OPS,
+#elif defined(CONFIG_PM_SLEEP)
+ .suspend = atl1_suspend_compat,
+ .resume = atl1_resume_compat,
+#endif
+};
+
+/*
+ * atl1_exit_module - Driver Exit Cleanup Routine
+ *
+ * atl1_exit_module is called just before the driver is removed
+ * from memory.
+ */
+static void __exit atl1_exit_module(void)
+{
+ pci_unregister_driver(&atl1_driver);
+}
+
+/*
+ * atl1_init_module - Driver Registration Routine
+ *
+ * atl1_init_module is the first routine called when the driver is
+ * loaded. All it does is register with the PCI subsystem.
+ */
+static int __init atl1_init_module(void)
+{
+ return pci_register_driver(&atl1_driver);
+}
+
+module_init(atl1_init_module);
+module_exit(atl1_exit_module);
+
+struct atl1_stats {
+ char stat_string[ETH_GSTRING_LEN];
+ int sizeof_stat;
+ int stat_offset;
+};
+
+#define ATL1_STAT(m) \
+ sizeof(((struct atl1_adapter *)0)->m), offsetof(struct atl1_adapter, m)
+
+static struct atl1_stats atl1_gstrings_stats[] = {
+ {"rx_packets", ATL1_STAT(soft_stats.rx_packets)},
+ {"tx_packets", ATL1_STAT(soft_stats.tx_packets)},
+ {"rx_bytes", ATL1_STAT(soft_stats.rx_bytes)},
+ {"tx_bytes", ATL1_STAT(soft_stats.tx_bytes)},
+ {"rx_errors", ATL1_STAT(soft_stats.rx_errors)},
+ {"tx_errors", ATL1_STAT(soft_stats.tx_errors)},
+ {"multicast", ATL1_STAT(soft_stats.multicast)},
+ {"collisions", ATL1_STAT(soft_stats.collisions)},
+ {"rx_length_errors", ATL1_STAT(soft_stats.rx_length_errors)},
+ {"rx_over_errors", ATL1_STAT(soft_stats.rx_missed_errors)},
+ {"rx_crc_errors", ATL1_STAT(soft_stats.rx_crc_errors)},
+ {"rx_frame_errors", ATL1_STAT(soft_stats.rx_frame_errors)},
+ {"rx_fifo_errors", ATL1_STAT(soft_stats.rx_fifo_errors)},
+ {"rx_missed_errors", ATL1_STAT(soft_stats.rx_missed_errors)},
+ {"tx_aborted_errors", ATL1_STAT(soft_stats.tx_aborted_errors)},
+ {"tx_carrier_errors", ATL1_STAT(soft_stats.tx_carrier_errors)},
+ {"tx_fifo_errors", ATL1_STAT(soft_stats.tx_fifo_errors)},
+ {"tx_window_errors", ATL1_STAT(soft_stats.tx_window_errors)},
+ {"tx_abort_exce_coll", ATL1_STAT(soft_stats.excecol)},
+ {"tx_abort_late_coll", ATL1_STAT(soft_stats.latecol)},
+ {"tx_deferred_ok", ATL1_STAT(soft_stats.deffer)},
+ {"tx_single_coll_ok", ATL1_STAT(soft_stats.scc)},
+ {"tx_multi_coll_ok", ATL1_STAT(soft_stats.mcc)},
+ {"tx_underun", ATL1_STAT(soft_stats.tx_underun)},
+ {"tx_trunc", ATL1_STAT(soft_stats.tx_trunc)},
+ {"tx_pause", ATL1_STAT(soft_stats.tx_pause)},
+ {"rx_pause", ATL1_STAT(soft_stats.rx_pause)},
+ {"rx_rrd_ov", ATL1_STAT(soft_stats.rx_rrd_ov)},
+ {"rx_trunc", ATL1_STAT(soft_stats.rx_trunc)}
+};
+
+static void atl1_get_ethtool_stats(struct net_device *netdev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ int i;
+ char *p;
+
+ for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) {
+ p = (char *)adapter+atl1_gstrings_stats[i].stat_offset;
+ data[i] = (atl1_gstrings_stats[i].sizeof_stat ==
+ sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+ }
+
+}
+
+static int atl1_get_sset_count(struct net_device *netdev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return ARRAY_SIZE(atl1_gstrings_stats);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int atl1_get_settings(struct net_device *netdev,
+ struct ethtool_cmd *ecmd)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_hw *hw = &adapter->hw;
+
+ ecmd->supported = (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_1000baseT_Full |
+ SUPPORTED_Autoneg | SUPPORTED_TP);
+ ecmd->advertising = ADVERTISED_TP;
+ if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+ hw->media_type == MEDIA_TYPE_1000M_FULL) {
+ ecmd->advertising |= ADVERTISED_Autoneg;
+ if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR) {
+ ecmd->advertising |= ADVERTISED_Autoneg;
+ ecmd->advertising |=
+ (ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full |
+ ADVERTISED_1000baseT_Full);
+ } else
+ ecmd->advertising |= (ADVERTISED_1000baseT_Full);
+ }
+ ecmd->port = PORT_TP;
+ ecmd->phy_address = 0;
+ ecmd->transceiver = XCVR_INTERNAL;
+
+ if (netif_carrier_ok(adapter->netdev)) {
+ u16 link_speed, link_duplex;
+ atl1_get_speed_and_duplex(hw, &link_speed, &link_duplex);
+ ethtool_cmd_speed_set(ecmd, link_speed);
+ if (link_duplex == FULL_DUPLEX)
+ ecmd->duplex = DUPLEX_FULL;
+ else
+ ecmd->duplex = DUPLEX_HALF;
+ } else {
+ ethtool_cmd_speed_set(ecmd, -1);
+ ecmd->duplex = -1;
+ }
+ if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+ hw->media_type == MEDIA_TYPE_1000M_FULL)
+ ecmd->autoneg = AUTONEG_ENABLE;
+ else
+ ecmd->autoneg = AUTONEG_DISABLE;
+
+ return 0;
+}
+
+static int atl1_set_settings(struct net_device *netdev,
+ struct ethtool_cmd *ecmd)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_hw *hw = &adapter->hw;
+ u16 phy_data;
+ int ret_val = 0;
+ u16 old_media_type = hw->media_type;
+
+ if (netif_running(adapter->netdev)) {
+ if (netif_msg_link(adapter))
+ dev_dbg(&adapter->pdev->dev,
+ "ethtool shutting down adapter\n");
+ atl1_down(adapter);
+ }
+
+ if (ecmd->autoneg == AUTONEG_ENABLE)
+ hw->media_type = MEDIA_TYPE_AUTO_SENSOR;
+ else {
+ u32 speed = ethtool_cmd_speed(ecmd);
+ if (speed == SPEED_1000) {
+ if (ecmd->duplex != DUPLEX_FULL) {
+ if (netif_msg_link(adapter))
+ dev_warn(&adapter->pdev->dev,
+ "1000M half is invalid\n");
+ ret_val = -EINVAL;
+ goto exit_sset;
+ }
+ hw->media_type = MEDIA_TYPE_1000M_FULL;
+ } else if (speed == SPEED_100) {
+ if (ecmd->duplex == DUPLEX_FULL)
+ hw->media_type = MEDIA_TYPE_100M_FULL;
+ else
+ hw->media_type = MEDIA_TYPE_100M_HALF;
+ } else {
+ if (ecmd->duplex == DUPLEX_FULL)
+ hw->media_type = MEDIA_TYPE_10M_FULL;
+ else
+ hw->media_type = MEDIA_TYPE_10M_HALF;
+ }
+ }
+ switch (hw->media_type) {
+ case MEDIA_TYPE_AUTO_SENSOR:
+ ecmd->advertising =
+ ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full |
+ ADVERTISED_1000baseT_Full |
+ ADVERTISED_Autoneg | ADVERTISED_TP;
+ break;
+ case MEDIA_TYPE_1000M_FULL:
+ ecmd->advertising =
+ ADVERTISED_1000baseT_Full |
+ ADVERTISED_Autoneg | ADVERTISED_TP;
+ break;
+ default:
+ ecmd->advertising = 0;
+ break;
+ }
+ if (atl1_phy_setup_autoneg_adv(hw)) {
+ ret_val = -EINVAL;
+ if (netif_msg_link(adapter))
+ dev_warn(&adapter->pdev->dev,
+ "invalid ethtool speed/duplex setting\n");
+ goto exit_sset;
+ }
+ if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+ hw->media_type == MEDIA_TYPE_1000M_FULL)
+ phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN;
+ else {
+ switch (hw->media_type) {
+ case MEDIA_TYPE_100M_FULL:
+ phy_data =
+ MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 |
+ MII_CR_RESET;
+ break;
+ case MEDIA_TYPE_100M_HALF:
+ phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
+ break;
+ case MEDIA_TYPE_10M_FULL:
+ phy_data =
+ MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET;
+ break;
+ default:
+ /* MEDIA_TYPE_10M_HALF: */
+ phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
+ break;
+ }
+ }
+ atl1_write_phy_reg(hw, MII_BMCR, phy_data);
+exit_sset:
+ if (ret_val)
+ hw->media_type = old_media_type;
+
+ if (netif_running(adapter->netdev)) {
+ if (netif_msg_link(adapter))
+ dev_dbg(&adapter->pdev->dev,
+ "ethtool starting adapter\n");
+ atl1_up(adapter);
+ } else if (!ret_val) {
+ if (netif_msg_link(adapter))
+ dev_dbg(&adapter->pdev->dev,
+ "ethtool resetting adapter\n");
+ atl1_reset(adapter);
+ }
+ return ret_val;
+}
+
+static void atl1_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+
+ strlcpy(drvinfo->driver, ATLX_DRIVER_NAME, sizeof(drvinfo->driver));
+ strlcpy(drvinfo->version, ATLX_DRIVER_VERSION,
+ sizeof(drvinfo->version));
+ strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
+ strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
+ sizeof(drvinfo->bus_info));
+ drvinfo->eedump_len = ATL1_EEDUMP_LEN;
+}
+
+static void atl1_get_wol(struct net_device *netdev,
+ struct ethtool_wolinfo *wol)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+
+ wol->supported = WAKE_MAGIC;
+ wol->wolopts = 0;
+ if (adapter->wol & ATLX_WUFC_MAG)
+ wol->wolopts |= WAKE_MAGIC;
+}
+
+static int atl1_set_wol(struct net_device *netdev,
+ struct ethtool_wolinfo *wol)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+
+ if (wol->wolopts & (WAKE_PHY | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST |
+ WAKE_ARP | WAKE_MAGICSECURE))
+ return -EOPNOTSUPP;
+ adapter->wol = 0;
+ if (wol->wolopts & WAKE_MAGIC)
+ adapter->wol |= ATLX_WUFC_MAG;
+
+ device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
+
+ return 0;
+}
+
+static u32 atl1_get_msglevel(struct net_device *netdev)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ return adapter->msg_enable;
+}
+
+static void atl1_set_msglevel(struct net_device *netdev, u32 value)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ adapter->msg_enable = value;
+}
+
+static int atl1_get_regs_len(struct net_device *netdev)
+{
+ return ATL1_REG_COUNT * sizeof(u32);
+}
+
+static void atl1_get_regs(struct net_device *netdev, struct ethtool_regs *regs,
+ void *p)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_hw *hw = &adapter->hw;
+ unsigned int i;
+ u32 *regbuf = p;
+
+ for (i = 0; i < ATL1_REG_COUNT; i++) {
+ /*
+ * This switch statement avoids reserved regions
+ * of register space.
+ */
+ switch (i) {
+ case 6 ... 9:
+ case 14:
+ case 29 ... 31:
+ case 34 ... 63:
+ case 75 ... 127:
+ case 136 ... 1023:
+ case 1027 ... 1087:
+ case 1091 ... 1151:
+ case 1194 ... 1195:
+ case 1200 ... 1201:
+ case 1206 ... 1213:
+ case 1216 ... 1279:
+ case 1290 ... 1311:
+ case 1323 ... 1343:
+ case 1358 ... 1359:
+ case 1368 ... 1375:
+ case 1378 ... 1383:
+ case 1388 ... 1391:
+ case 1393 ... 1395:
+ case 1402 ... 1403:
+ case 1410 ... 1471:
+ case 1522 ... 1535:
+ /* reserved region; don't read it */
+ regbuf[i] = 0;
+ break;
+ default:
+ /* unreserved region */
+ regbuf[i] = ioread32(hw->hw_addr + (i * sizeof(u32)));
+ }
+ }
+}
+
+static void atl1_get_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_tpd_ring *txdr = &adapter->tpd_ring;
+ struct atl1_rfd_ring *rxdr = &adapter->rfd_ring;
+
+ ring->rx_max_pending = ATL1_MAX_RFD;
+ ring->tx_max_pending = ATL1_MAX_TPD;
+ ring->rx_pending = rxdr->count;
+ ring->tx_pending = txdr->count;
+}
+
+static int atl1_set_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_tpd_ring *tpdr = &adapter->tpd_ring;
+ struct atl1_rrd_ring *rrdr = &adapter->rrd_ring;
+ struct atl1_rfd_ring *rfdr = &adapter->rfd_ring;
+
+ struct atl1_tpd_ring tpd_old, tpd_new;
+ struct atl1_rfd_ring rfd_old, rfd_new;
+ struct atl1_rrd_ring rrd_old, rrd_new;
+ struct atl1_ring_header rhdr_old, rhdr_new;
+ struct atl1_smb smb;
+ struct atl1_cmb cmb;
+ int err;
+
+ tpd_old = adapter->tpd_ring;
+ rfd_old = adapter->rfd_ring;
+ rrd_old = adapter->rrd_ring;
+ rhdr_old = adapter->ring_header;
+
+ if (netif_running(adapter->netdev))
+ atl1_down(adapter);
+
+ rfdr->count = (u16) max(ring->rx_pending, (u32) ATL1_MIN_RFD);
+ rfdr->count = rfdr->count > ATL1_MAX_RFD ? ATL1_MAX_RFD :
+ rfdr->count;
+ rfdr->count = (rfdr->count + 3) & ~3;
+ rrdr->count = rfdr->count;
+
+ tpdr->count = (u16) max(ring->tx_pending, (u32) ATL1_MIN_TPD);
+ tpdr->count = tpdr->count > ATL1_MAX_TPD ? ATL1_MAX_TPD :
+ tpdr->count;
+ tpdr->count = (tpdr->count + 3) & ~3;
+
+ if (netif_running(adapter->netdev)) {
+ /* try to get new resources before deleting old */
+ err = atl1_setup_ring_resources(adapter);
+ if (err)
+ goto err_setup_ring;
+
+ /*
+ * save the new, restore the old in order to free it,
+ * then restore the new back again
+ */
+
+ rfd_new = adapter->rfd_ring;
+ rrd_new = adapter->rrd_ring;
+ tpd_new = adapter->tpd_ring;
+ rhdr_new = adapter->ring_header;
+ adapter->rfd_ring = rfd_old;
+ adapter->rrd_ring = rrd_old;
+ adapter->tpd_ring = tpd_old;
+ adapter->ring_header = rhdr_old;
+ /*
+ * Save SMB and CMB, since atl1_free_ring_resources
+ * will clear them.
+ */
+ smb = adapter->smb;
+ cmb = adapter->cmb;
+ atl1_free_ring_resources(adapter);
+ adapter->rfd_ring = rfd_new;
+ adapter->rrd_ring = rrd_new;
+ adapter->tpd_ring = tpd_new;
+ adapter->ring_header = rhdr_new;
+ adapter->smb = smb;
+ adapter->cmb = cmb;
+
+ err = atl1_up(adapter);
+ if (err)
+ return err;
+ }
+ return 0;
+
+err_setup_ring:
+ adapter->rfd_ring = rfd_old;
+ adapter->rrd_ring = rrd_old;
+ adapter->tpd_ring = tpd_old;
+ adapter->ring_header = rhdr_old;
+ atl1_up(adapter);
+ return err;
+}
+
+static void atl1_get_pauseparam(struct net_device *netdev,
+ struct ethtool_pauseparam *epause)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_hw *hw = &adapter->hw;
+
+ if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+ hw->media_type == MEDIA_TYPE_1000M_FULL) {
+ epause->autoneg = AUTONEG_ENABLE;
+ } else {
+ epause->autoneg = AUTONEG_DISABLE;
+ }
+ epause->rx_pause = 1;
+ epause->tx_pause = 1;
+}
+
+static int atl1_set_pauseparam(struct net_device *netdev,
+ struct ethtool_pauseparam *epause)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_hw *hw = &adapter->hw;
+
+ if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+ hw->media_type == MEDIA_TYPE_1000M_FULL) {
+ epause->autoneg = AUTONEG_ENABLE;
+ } else {
+ epause->autoneg = AUTONEG_DISABLE;
+ }
+
+ epause->rx_pause = 1;
+ epause->tx_pause = 1;
+
+ return 0;
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39))
+/* FIXME: is this right? -- CHS */
+static u32 atl1_get_rx_csum(struct net_device *netdev)
+{
+ return 1;
+}
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */
+
+static void atl1_get_strings(struct net_device *netdev, u32 stringset,
+ u8 *data)
+{
+ u8 *p = data;
+ int i;
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) {
+ memcpy(p, atl1_gstrings_stats[i].stat_string,
+ ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
+ break;
+ }
+}
+
+static int atl1_nway_reset(struct net_device *netdev)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_hw *hw = &adapter->hw;
+
+ if (netif_running(netdev)) {
+ u16 phy_data;
+ atl1_down(adapter);
+
+ if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+ hw->media_type == MEDIA_TYPE_1000M_FULL) {
+ phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN;
+ } else {
+ switch (hw->media_type) {
+ case MEDIA_TYPE_100M_FULL:
+ phy_data = MII_CR_FULL_DUPLEX |
+ MII_CR_SPEED_100 | MII_CR_RESET;
+ break;
+ case MEDIA_TYPE_100M_HALF:
+ phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
+ break;
+ case MEDIA_TYPE_10M_FULL:
+ phy_data = MII_CR_FULL_DUPLEX |
+ MII_CR_SPEED_10 | MII_CR_RESET;
+ break;
+ default:
+ /* MEDIA_TYPE_10M_HALF */
+ phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
+ }
+ }
+ atl1_write_phy_reg(hw, MII_BMCR, phy_data);
+ atl1_up(adapter);
+ }
+ return 0;
+}
+
+static const struct ethtool_ops atl1_ethtool_ops = {
+ .get_settings = atl1_get_settings,
+ .set_settings = atl1_set_settings,
+ .get_drvinfo = atl1_get_drvinfo,
+ .get_wol = atl1_get_wol,
+ .set_wol = atl1_set_wol,
+ .get_msglevel = atl1_get_msglevel,
+ .set_msglevel = atl1_set_msglevel,
+ .get_regs_len = atl1_get_regs_len,
+ .get_regs = atl1_get_regs,
+ .get_ringparam = atl1_get_ringparam,
+ .set_ringparam = atl1_set_ringparam,
+ .get_pauseparam = atl1_get_pauseparam,
+ .set_pauseparam = atl1_set_pauseparam,
+ .get_link = ethtool_op_get_link,
+ .get_strings = atl1_get_strings,
+ .nway_reset = atl1_nway_reset,
+ .get_ethtool_stats = atl1_get_ethtool_stats,
+ .get_sset_count = atl1_get_sset_count,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39))
+ .get_rx_csum = atl1_get_rx_csum,
+ .set_tx_csum = ethtool_op_set_tx_hw_csum,
+ .set_sg = ethtool_op_set_sg,
+ .set_tso = ethtool_op_set_tso,
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */
+};
diff --git a/drivers/net/ethernet/atheros/atlx/atl1.h b/drivers/net/ethernet/atheros/atlx/atl1.h
new file mode 100644
index 0000000..109d6da
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atlx/atl1.h
@@ -0,0 +1,792 @@
+/*
+ * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved.
+ * Copyright(c) 2006 - 2007 Chris Snook <csnook@redhat.com>
+ * Copyright(c) 2006 - 2008 Jay Cliburn <jcliburn@gmail.com>
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ */
+
+#ifndef ATL1_H
+#define ATL1_H
+
+#include <linux/compiler.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include "atlx.h"
+
+#define ATLX_DRIVER_NAME "atl1"
+
+MODULE_DESCRIPTION("Atheros L1 Gigabit Ethernet Driver");
+
+#define atlx_adapter atl1_adapter
+#define atlx_check_for_link atl1_check_for_link
+#define atlx_check_link atl1_check_link
+#define atlx_hash_mc_addr atl1_hash_mc_addr
+#define atlx_hash_set atl1_hash_set
+#define atlx_hw atl1_hw
+#define atlx_mii_ioctl atl1_mii_ioctl
+#define atlx_read_phy_reg atl1_read_phy_reg
+#define atlx_set_mac atl1_set_mac
+#define atlx_set_mac_addr atl1_set_mac_addr
+
+struct atl1_adapter;
+struct atl1_hw;
+
+/* function prototypes needed by multiple files */
+static u32 atl1_hash_mc_addr(struct atl1_hw *hw, u8 *mc_addr);
+static void atl1_hash_set(struct atl1_hw *hw, u32 hash_value);
+static void atl1_set_mac_addr(struct atl1_hw *hw);
+static int atl1_mii_ioctl(struct net_device *netdev, struct ifreq *ifr,
+ int cmd);
+static u32 atl1_check_link(struct atl1_adapter *adapter);
+
+/* hardware definitions specific to L1 */
+
+/* Block IDLE Status Register */
+#define IDLE_STATUS_RXMAC 0x1
+#define IDLE_STATUS_TXMAC 0x2
+#define IDLE_STATUS_RXQ 0x4
+#define IDLE_STATUS_TXQ 0x8
+#define IDLE_STATUS_DMAR 0x10
+#define IDLE_STATUS_DMAW 0x20
+#define IDLE_STATUS_SMB 0x40
+#define IDLE_STATUS_CMB 0x80
+
+/* MDIO Control Register */
+#define MDIO_WAIT_TIMES 30
+
+/* MAC Control Register */
+#define MAC_CTRL_TX_PAUSE 0x10000
+#define MAC_CTRL_SCNT 0x20000
+#define MAC_CTRL_SRST_TX 0x40000
+#define MAC_CTRL_TX_SIMURST 0x80000
+#define MAC_CTRL_SPEED_SHIFT 20
+#define MAC_CTRL_SPEED_MASK 0x300000
+#define MAC_CTRL_SPEED_1000 0x2
+#define MAC_CTRL_SPEED_10_100 0x1
+#define MAC_CTRL_DBG_TX_BKPRESURE 0x400000
+#define MAC_CTRL_TX_HUGE 0x800000
+#define MAC_CTRL_RX_CHKSUM_EN 0x1000000
+#define MAC_CTRL_DBG 0x8000000
+
+/* Wake-On-Lan control register */
+#define WOL_CLK_SWITCH_EN 0x8000
+#define WOL_PT5_EN 0x200000
+#define WOL_PT6_EN 0x400000
+#define WOL_PT5_MATCH 0x8000000
+#define WOL_PT6_MATCH 0x10000000
+
+/* WOL Length ( 2 DWORD ) */
+#define REG_WOL_PATTERN_LEN 0x14A4
+#define WOL_PT_LEN_MASK 0x7F
+#define WOL_PT0_LEN_SHIFT 0
+#define WOL_PT1_LEN_SHIFT 8
+#define WOL_PT2_LEN_SHIFT 16
+#define WOL_PT3_LEN_SHIFT 24
+#define WOL_PT4_LEN_SHIFT 0
+#define WOL_PT5_LEN_SHIFT 8
+#define WOL_PT6_LEN_SHIFT 16
+
+/* Internal SRAM Partition Registers, low 32 bits */
+#define REG_SRAM_RFD_LEN 0x1504
+#define REG_SRAM_RRD_ADDR 0x1508
+#define REG_SRAM_RRD_LEN 0x150C
+#define REG_SRAM_TPD_ADDR 0x1510
+#define REG_SRAM_TPD_LEN 0x1514
+#define REG_SRAM_TRD_ADDR 0x1518
+#define REG_SRAM_TRD_LEN 0x151C
+#define REG_SRAM_RXF_ADDR 0x1520
+#define REG_SRAM_RXF_LEN 0x1524
+#define REG_SRAM_TXF_ADDR 0x1528
+#define REG_SRAM_TXF_LEN 0x152C
+#define REG_SRAM_TCPH_PATH_ADDR 0x1530
+#define SRAM_TCPH_ADDR_MASK 0xFFF
+#define SRAM_TCPH_ADDR_SHIFT 0
+#define SRAM_PATH_ADDR_MASK 0xFFF
+#define SRAM_PATH_ADDR_SHIFT 16
+
+/* Load Ptr Register */
+#define REG_LOAD_PTR 0x1534
+
+/* Descriptor Control registers, low 32 bits */
+#define REG_DESC_RFD_ADDR_LO 0x1544
+#define REG_DESC_RRD_ADDR_LO 0x1548
+#define REG_DESC_TPD_ADDR_LO 0x154C
+#define REG_DESC_CMB_ADDR_LO 0x1550
+#define REG_DESC_SMB_ADDR_LO 0x1554
+#define REG_DESC_RFD_RRD_RING_SIZE 0x1558
+#define DESC_RFD_RING_SIZE_MASK 0x7FF
+#define DESC_RFD_RING_SIZE_SHIFT 0
+#define DESC_RRD_RING_SIZE_MASK 0x7FF
+#define DESC_RRD_RING_SIZE_SHIFT 16
+#define REG_DESC_TPD_RING_SIZE 0x155C
+#define DESC_TPD_RING_SIZE_MASK 0x3FF
+#define DESC_TPD_RING_SIZE_SHIFT 0
+
+/* TXQ Control Register */
+#define REG_TXQ_CTRL 0x1580
+#define TXQ_CTRL_TPD_BURST_NUM_SHIFT 0
+#define TXQ_CTRL_TPD_BURST_NUM_MASK 0x1F
+#define TXQ_CTRL_EN 0x20
+#define TXQ_CTRL_ENH_MODE 0x40
+#define TXQ_CTRL_TPD_FETCH_TH_SHIFT 8
+#define TXQ_CTRL_TPD_FETCH_TH_MASK 0x3F
+#define TXQ_CTRL_TXF_BURST_NUM_SHIFT 16
+#define TXQ_CTRL_TXF_BURST_NUM_MASK 0xFFFF
+
+/* Jumbo packet Threshold for task offload */
+#define REG_TX_JUMBO_TASK_TH_TPD_IPG 0x1584
+#define TX_JUMBO_TASK_TH_MASK 0x7FF
+#define TX_JUMBO_TASK_TH_SHIFT 0
+#define TX_TPD_MIN_IPG_MASK 0x1F
+#define TX_TPD_MIN_IPG_SHIFT 16
+
+/* RXQ Control Register */
+#define REG_RXQ_CTRL 0x15A0
+#define RXQ_CTRL_RFD_BURST_NUM_SHIFT 0
+#define RXQ_CTRL_RFD_BURST_NUM_MASK 0xFF
+#define RXQ_CTRL_RRD_BURST_THRESH_SHIFT 8
+#define RXQ_CTRL_RRD_BURST_THRESH_MASK 0xFF
+#define RXQ_CTRL_RFD_PREF_MIN_IPG_SHIFT 16
+#define RXQ_CTRL_RFD_PREF_MIN_IPG_MASK 0x1F
+#define RXQ_CTRL_CUT_THRU_EN 0x40000000
+#define RXQ_CTRL_EN 0x80000000
+
+/* Rx jumbo packet threshold and rrd retirement timer */
+#define REG_RXQ_JMBOSZ_RRDTIM 0x15A4
+#define RXQ_JMBOSZ_TH_MASK 0x7FF
+#define RXQ_JMBOSZ_TH_SHIFT 0
+#define RXQ_JMBO_LKAH_MASK 0xF
+#define RXQ_JMBO_LKAH_SHIFT 11
+#define RXQ_RRD_TIMER_MASK 0xFFFF
+#define RXQ_RRD_TIMER_SHIFT 16
+
+/* RFD flow control register */
+#define REG_RXQ_RXF_PAUSE_THRESH 0x15A8
+#define RXQ_RXF_PAUSE_TH_HI_SHIFT 16
+#define RXQ_RXF_PAUSE_TH_HI_MASK 0xFFF
+#define RXQ_RXF_PAUSE_TH_LO_SHIFT 0
+#define RXQ_RXF_PAUSE_TH_LO_MASK 0xFFF
+
+/* RRD flow control register */
+#define REG_RXQ_RRD_PAUSE_THRESH 0x15AC
+#define RXQ_RRD_PAUSE_TH_HI_SHIFT 0
+#define RXQ_RRD_PAUSE_TH_HI_MASK 0xFFF
+#define RXQ_RRD_PAUSE_TH_LO_SHIFT 16
+#define RXQ_RRD_PAUSE_TH_LO_MASK 0xFFF
+
+/* DMA Engine Control Register */
+#define REG_DMA_CTRL 0x15C0
+#define DMA_CTRL_DMAR_IN_ORDER 0x1
+#define DMA_CTRL_DMAR_ENH_ORDER 0x2
+#define DMA_CTRL_DMAR_OUT_ORDER 0x4
+#define DMA_CTRL_RCB_VALUE 0x8
+#define DMA_CTRL_DMAR_BURST_LEN_SHIFT 4
+#define DMA_CTRL_DMAR_BURST_LEN_MASK 7
+#define DMA_CTRL_DMAW_BURST_LEN_SHIFT 7
+#define DMA_CTRL_DMAW_BURST_LEN_MASK 7
+#define DMA_CTRL_DMAR_EN 0x400
+#define DMA_CTRL_DMAW_EN 0x800
+
+/* CMB/SMB Control Register */
+#define REG_CSMB_CTRL 0x15D0
+#define CSMB_CTRL_CMB_NOW 1
+#define CSMB_CTRL_SMB_NOW 2
+#define CSMB_CTRL_CMB_EN 4
+#define CSMB_CTRL_SMB_EN 8
+
+/* CMB DMA Write Threshold Register */
+#define REG_CMB_WRITE_TH 0x15D4
+#define CMB_RRD_TH_SHIFT 0
+#define CMB_RRD_TH_MASK 0x7FF
+#define CMB_TPD_TH_SHIFT 16
+#define CMB_TPD_TH_MASK 0x7FF
+
+/* RX/TX count-down timer to trigger CMB-write. 2us resolution. */
+#define REG_CMB_WRITE_TIMER 0x15D8
+#define CMB_RX_TM_SHIFT 0
+#define CMB_RX_TM_MASK 0xFFFF
+#define CMB_TX_TM_SHIFT 16
+#define CMB_TX_TM_MASK 0xFFFF
+
+/* Number of packet received since last CMB write */
+#define REG_CMB_RX_PKT_CNT 0x15DC
+
+/* Number of packet transmitted since last CMB write */
+#define REG_CMB_TX_PKT_CNT 0x15E0
+
+/* SMB auto DMA timer register */
+#define REG_SMB_TIMER 0x15E4
+
+/* Mailbox Register */
+#define REG_MAILBOX 0x15F0
+#define MB_RFD_PROD_INDX_SHIFT 0
+#define MB_RFD_PROD_INDX_MASK 0x7FF
+#define MB_RRD_CONS_INDX_SHIFT 11
+#define MB_RRD_CONS_INDX_MASK 0x7FF
+#define MB_TPD_PROD_INDX_SHIFT 22
+#define MB_TPD_PROD_INDX_MASK 0x3FF
+
+/* Interrupt Status Register */
+#define ISR_SMB 0x1
+#define ISR_TIMER 0x2
+#define ISR_MANUAL 0x4
+#define ISR_RXF_OV 0x8
+#define ISR_RFD_UNRUN 0x10
+#define ISR_RRD_OV 0x20
+#define ISR_TXF_UNRUN 0x40
+#define ISR_LINK 0x80
+#define ISR_HOST_RFD_UNRUN 0x100
+#define ISR_HOST_RRD_OV 0x200
+#define ISR_DMAR_TO_RST 0x400
+#define ISR_DMAW_TO_RST 0x800
+#define ISR_GPHY 0x1000
+#define ISR_RX_PKT 0x10000
+#define ISR_TX_PKT 0x20000
+#define ISR_TX_DMA 0x40000
+#define ISR_RX_DMA 0x80000
+#define ISR_CMB_RX 0x100000
+#define ISR_CMB_TX 0x200000
+#define ISR_MAC_RX 0x400000
+#define ISR_MAC_TX 0x800000
+#define ISR_DIS_SMB 0x20000000
+#define ISR_DIS_DMA 0x40000000
+
+/* Normal Interrupt mask */
+#define IMR_NORMAL_MASK (\
+ ISR_SMB |\
+ ISR_GPHY |\
+ ISR_PHY_LINKDOWN|\
+ ISR_DMAR_TO_RST |\
+ ISR_DMAW_TO_RST |\
+ ISR_CMB_TX |\
+ ISR_CMB_RX)
+
+/* Debug Interrupt Mask (enable all interrupt) */
+#define IMR_DEBUG_MASK (\
+ ISR_SMB |\
+ ISR_TIMER |\
+ ISR_MANUAL |\
+ ISR_RXF_OV |\
+ ISR_RFD_UNRUN |\
+ ISR_RRD_OV |\
+ ISR_TXF_UNRUN |\
+ ISR_LINK |\
+ ISR_CMB_TX |\
+ ISR_CMB_RX |\
+ ISR_RX_PKT |\
+ ISR_TX_PKT |\
+ ISR_MAC_RX |\
+ ISR_MAC_TX)
+
+#define MEDIA_TYPE_1000M_FULL 1
+#define MEDIA_TYPE_100M_FULL 2
+#define MEDIA_TYPE_100M_HALF 3
+#define MEDIA_TYPE_10M_FULL 4
+#define MEDIA_TYPE_10M_HALF 5
+
+#define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x002F /* All but 1000-Half */
+
+#define MAX_JUMBO_FRAME_SIZE 10240
+
+#define ATL1_EEDUMP_LEN 48
+
+/* Statistics counters collected by the MAC */
+struct stats_msg_block {
+ /* rx */
+ u32 rx_ok; /* good RX packets */
+ u32 rx_bcast; /* good RX broadcast packets */
+ u32 rx_mcast; /* good RX multicast packets */
+ u32 rx_pause; /* RX pause frames */
+ u32 rx_ctrl; /* RX control packets other than pause frames */
+ u32 rx_fcs_err; /* RX packets with bad FCS */
+ u32 rx_len_err; /* RX packets with length != actual size */
+ u32 rx_byte_cnt; /* good bytes received. FCS is NOT included */
+ u32 rx_runt; /* RX packets < 64 bytes with good FCS */
+ u32 rx_frag; /* RX packets < 64 bytes with bad FCS */
+ u32 rx_sz_64; /* 64 byte RX packets */
+ u32 rx_sz_65_127;
+ u32 rx_sz_128_255;
+ u32 rx_sz_256_511;
+ u32 rx_sz_512_1023;
+ u32 rx_sz_1024_1518;
+ u32 rx_sz_1519_max; /* 1519 byte to MTU RX packets */
+ u32 rx_sz_ov; /* truncated RX packets > MTU */
+ u32 rx_rxf_ov; /* frames dropped due to RX FIFO overflow */
+ u32 rx_rrd_ov; /* frames dropped due to RRD overflow */
+ u32 rx_align_err; /* alignment errors */
+ u32 rx_bcast_byte_cnt; /* RX broadcast bytes, excluding FCS */
+ u32 rx_mcast_byte_cnt; /* RX multicast bytes, excluding FCS */
+ u32 rx_err_addr; /* packets dropped due to address filtering */
+
+ /* tx */
+ u32 tx_ok; /* good TX packets */
+ u32 tx_bcast; /* good TX broadcast packets */
+ u32 tx_mcast; /* good TX multicast packets */
+ u32 tx_pause; /* TX pause frames */
+ u32 tx_exc_defer; /* TX packets deferred excessively */
+ u32 tx_ctrl; /* TX control frames, excluding pause frames */
+ u32 tx_defer; /* TX packets deferred */
+ u32 tx_byte_cnt; /* bytes transmitted, FCS is NOT included */
+ u32 tx_sz_64; /* 64 byte TX packets */
+ u32 tx_sz_65_127;
+ u32 tx_sz_128_255;
+ u32 tx_sz_256_511;
+ u32 tx_sz_512_1023;
+ u32 tx_sz_1024_1518;
+ u32 tx_sz_1519_max; /* 1519 byte to MTU TX packets */
+ u32 tx_1_col; /* packets TX after a single collision */
+ u32 tx_2_col; /* packets TX after multiple collisions */
+ u32 tx_late_col; /* TX packets with late collisions */
+ u32 tx_abort_col; /* TX packets aborted w/excessive collisions */
+ u32 tx_underrun; /* TX packets aborted due to TX FIFO underrun
+ * or TRD FIFO underrun */
+ u32 tx_rd_eop; /* reads beyond the EOP into the next frame
+ * when TRD was not written timely */
+ u32 tx_len_err; /* TX packets where length != actual size */
+ u32 tx_trunc; /* TX packets truncated due to size > MTU */
+ u32 tx_bcast_byte; /* broadcast bytes transmitted, excluding FCS */
+ u32 tx_mcast_byte; /* multicast bytes transmitted, excluding FCS */
+ u32 smb_updated; /* 1: SMB Updated. This is used by software to
+ * indicate the statistics update. Software
+ * should clear this bit after retrieving the
+ * statistics information. */
+};
+
+/* Coalescing Message Block */
+struct coals_msg_block {
+ u32 int_stats; /* interrupt status */
+ u16 rrd_prod_idx; /* TRD Producer Index. */
+ u16 rfd_cons_idx; /* RFD Consumer Index. */
+ u16 update; /* Selene sets this bit every time it DMAs the
+ * CMB to host memory. Software should clear
+ * this bit when CMB info is processed. */
+ u16 tpd_cons_idx; /* TPD Consumer Index. */
+};
+
+/* RRD descriptor */
+struct rx_return_desc {
+ u8 num_buf; /* Number of RFD buffers used by the received packet */
+ u8 resved;
+ u16 buf_indx; /* RFD Index of the first buffer */
+ union {
+ u32 valid;
+ struct {
+ u16 rx_chksum;
+ u16 pkt_size;
+ } xsum_sz;
+ } xsz;
+
+ u16 pkt_flg; /* Packet flags */
+ u16 err_flg; /* Error flags */
+ u16 resved2;
+ u16 vlan_tag; /* VLAN TAG */
+};
+
+#define PACKET_FLAG_ETH_TYPE 0x0080
+#define PACKET_FLAG_VLAN_INS 0x0100
+#define PACKET_FLAG_ERR 0x0200
+#define PACKET_FLAG_IPV4 0x0400
+#define PACKET_FLAG_UDP 0x0800
+#define PACKET_FLAG_TCP 0x1000
+#define PACKET_FLAG_BCAST 0x2000
+#define PACKET_FLAG_MCAST 0x4000
+#define PACKET_FLAG_PAUSE 0x8000
+
+#define ERR_FLAG_CRC 0x0001
+#define ERR_FLAG_CODE 0x0002
+#define ERR_FLAG_DRIBBLE 0x0004
+#define ERR_FLAG_RUNT 0x0008
+#define ERR_FLAG_OV 0x0010
+#define ERR_FLAG_TRUNC 0x0020
+#define ERR_FLAG_IP_CHKSUM 0x0040
+#define ERR_FLAG_L4_CHKSUM 0x0080
+#define ERR_FLAG_LEN 0x0100
+#define ERR_FLAG_DES_ADDR 0x0200
+
+/* RFD descriptor */
+struct rx_free_desc {
+ __le64 buffer_addr; /* Address of the descriptor's data buffer */
+ __le16 buf_len; /* Size of the receive buffer in host memory */
+ u16 coalese; /* Update consumer index to host after the
+ * reception of this frame */
+ /* __packed is required */
+} __packed;
+
+/*
+ * The L1 transmit packet descriptor is comprised of four 32-bit words.
+ *
+ * 31 0
+ * +---------------------------------------+
+ * | Word 0: Buffer addr lo |
+ * +---------------------------------------+
+ * | Word 1: Buffer addr hi |
+ * +---------------------------------------+
+ * | Word 2 |
+ * +---------------------------------------+
+ * | Word 3 |
+ * +---------------------------------------+
+ *
+ * Words 0 and 1 combine to form a 64-bit buffer address.
+ *
+ * Word 2 is self explanatory in the #define block below.
+ *
+ * Word 3 has two forms, depending upon the state of bits 3 and 4.
+ * If bits 3 and 4 are both zero, then bits 14:31 are unused by the
+ * hardware. Otherwise, if either bit 3 or 4 is set, the definition
+ * of bits 14:31 vary according to the following depiction.
+ *
+ * 0 End of packet 0 End of packet
+ * 1 Coalesce 1 Coalesce
+ * 2 Insert VLAN tag 2 Insert VLAN tag
+ * 3 Custom csum enable = 0 3 Custom csum enable = 1
+ * 4 Segment enable = 1 4 Segment enable = 0
+ * 5 Generate IP checksum 5 Generate IP checksum
+ * 6 Generate TCP checksum 6 Generate TCP checksum
+ * 7 Generate UDP checksum 7 Generate UDP checksum
+ * 8 VLAN tagged 8 VLAN tagged
+ * 9 Ethernet frame type 9 Ethernet frame type
+ * 10-+ 10-+
+ * 11 | IP hdr length (10:13) 11 | IP hdr length (10:13)
+ * 12 | (num 32-bit words) 12 | (num 32-bit words)
+ * 13-+ 13-+
+ * 14-+ 14 Unused
+ * 15 | TCP hdr length (14:17) 15 Unused
+ * 16 | (num 32-bit words) 16-+
+ * 17-+ 17 |
+ * 18 Header TPD flag 18 |
+ * 19-+ 19 | Payload offset
+ * 20 | 20 | (16:23)
+ * 21 | 21 |
+ * 22 | 22 |
+ * 23 | 23-+
+ * 24 | 24-+
+ * 25 | MSS (19:31) 25 |
+ * 26 | 26 |
+ * 27 | 27 | Custom csum offset
+ * 28 | 28 | (24:31)
+ * 29 | 29 |
+ * 30 | 30 |
+ * 31-+ 31-+
+ */
+
+/* tpd word 2 */
+#define TPD_BUFLEN_MASK 0x3FFF
+#define TPD_BUFLEN_SHIFT 0
+#define TPD_DMAINT_MASK 0x0001
+#define TPD_DMAINT_SHIFT 14
+#define TPD_PKTNT_MASK 0x0001
+#define TPD_PKTINT_SHIFT 15
+#define TPD_VLANTAG_MASK 0xFFFF
+#define TPD_VLANTAG_SHIFT 16
+
+/* tpd word 3 bits 0:13 */
+#define TPD_EOP_MASK 0x0001
+#define TPD_EOP_SHIFT 0
+#define TPD_COALESCE_MASK 0x0001
+#define TPD_COALESCE_SHIFT 1
+#define TPD_INS_VL_TAG_MASK 0x0001
+#define TPD_INS_VL_TAG_SHIFT 2
+#define TPD_CUST_CSUM_EN_MASK 0x0001
+#define TPD_CUST_CSUM_EN_SHIFT 3
+#define TPD_SEGMENT_EN_MASK 0x0001
+#define TPD_SEGMENT_EN_SHIFT 4
+#define TPD_IP_CSUM_MASK 0x0001
+#define TPD_IP_CSUM_SHIFT 5
+#define TPD_TCP_CSUM_MASK 0x0001
+#define TPD_TCP_CSUM_SHIFT 6
+#define TPD_UDP_CSUM_MASK 0x0001
+#define TPD_UDP_CSUM_SHIFT 7
+#define TPD_VL_TAGGED_MASK 0x0001
+#define TPD_VL_TAGGED_SHIFT 8
+#define TPD_ETHTYPE_MASK 0x0001
+#define TPD_ETHTYPE_SHIFT 9
+#define TPD_IPHL_MASK 0x000F
+#define TPD_IPHL_SHIFT 10
+
+/* tpd word 3 bits 14:31 if segment enabled */
+#define TPD_TCPHDRLEN_MASK 0x000F
+#define TPD_TCPHDRLEN_SHIFT 14
+#define TPD_HDRFLAG_MASK 0x0001
+#define TPD_HDRFLAG_SHIFT 18
+#define TPD_MSS_MASK 0x1FFF
+#define TPD_MSS_SHIFT 19
+
+/* tpd word 3 bits 16:31 if custom csum enabled */
+#define TPD_PLOADOFFSET_MASK 0x00FF
+#define TPD_PLOADOFFSET_SHIFT 16
+#define TPD_CCSUMOFFSET_MASK 0x00FF
+#define TPD_CCSUMOFFSET_SHIFT 24
+
+struct tx_packet_desc {
+ __le64 buffer_addr;
+ __le32 word2;
+ __le32 word3;
+};
+
+/* DMA Order Settings */
+enum atl1_dma_order {
+ atl1_dma_ord_in = 1,
+ atl1_dma_ord_enh = 2,
+ atl1_dma_ord_out = 4
+};
+
+enum atl1_dma_rcb {
+ atl1_rcb_64 = 0,
+ atl1_rcb_128 = 1
+};
+
+enum atl1_dma_req_block {
+ atl1_dma_req_128 = 0,
+ atl1_dma_req_256 = 1,
+ atl1_dma_req_512 = 2,
+ atl1_dma_req_1024 = 3,
+ atl1_dma_req_2048 = 4,
+ atl1_dma_req_4096 = 5
+};
+
+#define ATL1_MAX_INTR 3
+#define ATL1_MAX_TX_BUF_LEN 0x3000 /* 12288 bytes */
+
+#define ATL1_DEFAULT_TPD 256
+#define ATL1_MAX_TPD 1024
+#define ATL1_MIN_TPD 64
+#define ATL1_DEFAULT_RFD 512
+#define ATL1_MIN_RFD 128
+#define ATL1_MAX_RFD 2048
+#define ATL1_REG_COUNT 1538
+
+#define ATL1_GET_DESC(R, i, type) (&(((type *)((R)->desc))[i]))
+#define ATL1_RFD_DESC(R, i) ATL1_GET_DESC(R, i, struct rx_free_desc)
+#define ATL1_TPD_DESC(R, i) ATL1_GET_DESC(R, i, struct tx_packet_desc)
+#define ATL1_RRD_DESC(R, i) ATL1_GET_DESC(R, i, struct rx_return_desc)
+
+/*
+ * atl1_ring_header represents a single, contiguous block of DMA space
+ * mapped for the three descriptor rings (tpd, rfd, rrd) and the two
+ * message blocks (cmb, smb) described below
+ */
+struct atl1_ring_header {
+ void *desc; /* virtual address */
+ dma_addr_t dma; /* physical address*/
+ unsigned int size; /* length in bytes */
+};
+
+/*
+ * atl1_buffer is wrapper around a pointer to a socket buffer
+ * so a DMA handle can be stored along with the skb
+ */
+struct atl1_buffer {
+ struct sk_buff *skb; /* socket buffer */
+ u16 length; /* rx buffer length */
+ u16 alloced; /* 1 if skb allocated */
+ dma_addr_t dma;
+};
+
+/* transmit packet descriptor (tpd) ring */
+struct atl1_tpd_ring {
+ void *desc; /* descriptor ring virtual address */
+ dma_addr_t dma; /* descriptor ring physical address */
+ u16 size; /* descriptor ring length in bytes */
+ u16 count; /* number of descriptors in the ring */
+ u16 hw_idx; /* hardware index */
+ atomic_t next_to_clean;
+ atomic_t next_to_use;
+ struct atl1_buffer *buffer_info;
+};
+
+/* receive free descriptor (rfd) ring */
+struct atl1_rfd_ring {
+ void *desc; /* descriptor ring virtual address */
+ dma_addr_t dma; /* descriptor ring physical address */
+ u16 size; /* descriptor ring length in bytes */
+ u16 count; /* number of descriptors in the ring */
+ atomic_t next_to_use;
+ u16 next_to_clean;
+ struct atl1_buffer *buffer_info;
+};
+
+/* receive return descriptor (rrd) ring */
+struct atl1_rrd_ring {
+ void *desc; /* descriptor ring virtual address */
+ dma_addr_t dma; /* descriptor ring physical address */
+ unsigned int size; /* descriptor ring length in bytes */
+ u16 count; /* number of descriptors in the ring */
+ u16 next_to_use;
+ atomic_t next_to_clean;
+};
+
+/* coalescing message block (cmb) */
+struct atl1_cmb {
+ struct coals_msg_block *cmb;
+ dma_addr_t dma;
+};
+
+/* statistics message block (smb) */
+struct atl1_smb {
+ struct stats_msg_block *smb;
+ dma_addr_t dma;
+};
+
+/* Statistics counters */
+struct atl1_sft_stats {
+ u64 rx_packets;
+ u64 tx_packets;
+ u64 rx_bytes;
+ u64 tx_bytes;
+ u64 multicast;
+ u64 collisions;
+ u64 rx_errors;
+ u64 rx_length_errors;
+ u64 rx_crc_errors;
+ u64 rx_frame_errors;
+ u64 rx_fifo_errors;
+ u64 rx_missed_errors;
+ u64 tx_errors;
+ u64 tx_fifo_errors;
+ u64 tx_aborted_errors;
+ u64 tx_window_errors;
+ u64 tx_carrier_errors;
+ u64 tx_pause; /* TX pause frames */
+ u64 excecol; /* TX packets w/ excessive collisions */
+ u64 deffer; /* TX packets deferred */
+ u64 scc; /* packets TX after a single collision */
+ u64 mcc; /* packets TX after multiple collisions */
+ u64 latecol; /* TX packets w/ late collisions */
+ u64 tx_underun; /* TX packets aborted due to TX FIFO underrun
+ * or TRD FIFO underrun */
+ u64 tx_trunc; /* TX packets truncated due to size > MTU */
+ u64 rx_pause; /* num Pause packets received. */
+ u64 rx_rrd_ov;
+ u64 rx_trunc;
+};
+
+/* hardware structure */
+struct atl1_hw {
+ u8 __iomem *hw_addr;
+ struct atl1_adapter *back;
+ enum atl1_dma_order dma_ord;
+ enum atl1_dma_rcb rcb_value;
+ enum atl1_dma_req_block dmar_block;
+ enum atl1_dma_req_block dmaw_block;
+ u8 preamble_len;
+ u8 max_retry;
+ u8 jam_ipg; /* IPG to start JAM for collision based flow
+ * control in half-duplex mode. In units of
+ * 8-bit time */
+ u8 ipgt; /* Desired back to back inter-packet gap.
+ * The default is 96-bit time */
+ u8 min_ifg; /* Minimum number of IFG to enforce in between
+ * receive frames. Frame gap below such IFP
+ * is dropped */
+ u8 ipgr1; /* 64bit Carrier-Sense window */
+ u8 ipgr2; /* 96-bit IPG window */
+ u8 tpd_burst; /* Number of TPD to prefetch in cache-aligned
+ * burst. Each TPD is 16 bytes long */
+ u8 rfd_burst; /* Number of RFD to prefetch in cache-aligned
+ * burst. Each RFD is 12 bytes long */
+ u8 rfd_fetch_gap;
+ u8 rrd_burst; /* Threshold number of RRDs that can be retired
+ * in a burst. Each RRD is 16 bytes long */
+ u8 tpd_fetch_th;
+ u8 tpd_fetch_gap;
+ u16 tx_jumbo_task_th;
+ u16 txf_burst; /* Number of data bytes to read in a cache-
+ * aligned burst. Each SRAM entry is 8 bytes */
+ u16 rx_jumbo_th; /* Jumbo packet size for non-VLAN packet. VLAN
+ * packets should add 4 bytes */
+ u16 rx_jumbo_lkah;
+ u16 rrd_ret_timer; /* RRD retirement timer. Decrement by 1 after
+ * every 512ns passes. */
+ u16 lcol; /* Collision Window */
+
+ u16 cmb_tpd;
+ u16 cmb_rrd;
+ u16 cmb_rx_timer;
+ u16 cmb_tx_timer;
+ u32 smb_timer;
+ u16 media_type;
+ u16 autoneg_advertised;
+
+ u16 mii_autoneg_adv_reg;
+ u16 mii_1000t_ctrl_reg;
+
+ u32 max_frame_size;
+ u32 min_frame_size;
+
+ u16 dev_rev;
+
+ /* spi flash */
+ u8 flash_vendor;
+
+ u8 mac_addr[ETH_ALEN];
+ u8 perm_mac_addr[ETH_ALEN];
+
+ bool phy_configured;
+};
+
+struct atl1_adapter {
+ struct net_device *netdev;
+ struct pci_dev *pdev;
+
+ struct atl1_sft_stats soft_stats;
+ u32 rx_buffer_len;
+ u32 wol;
+ u16 link_speed;
+ u16 link_duplex;
+ spinlock_t lock;
+ struct work_struct tx_timeout_task;
+ struct work_struct link_chg_task;
+ struct work_struct pcie_dma_to_rst_task;
+
+ struct timer_list phy_config_timer;
+ bool phy_timer_pending;
+
+ /* all descriptor rings' memory */
+ struct atl1_ring_header ring_header;
+
+ /* TX */
+ struct atl1_tpd_ring tpd_ring;
+ spinlock_t mb_lock;
+
+ /* RX */
+ struct atl1_rfd_ring rfd_ring;
+ struct atl1_rrd_ring rrd_ring;
+ u64 hw_csum_err;
+ u64 hw_csum_good;
+ u32 msg_enable;
+ u16 imt; /* interrupt moderator timer (2us resolution) */
+ u16 ict; /* interrupt clear timer (2us resolution */
+ struct mii_if_info mii; /* MII interface info */
+
+ u32 bd_number; /* board number */
+ bool pci_using_64;
+ struct atl1_hw hw;
+ struct atl1_smb smb;
+ struct atl1_cmb cmb;
+};
+
+#endif /* ATL1_H */
diff --git a/drivers/net/ethernet/atheros/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c
new file mode 100644
index 0000000..ccadd7e
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atlx/atl2.c
@@ -0,0 +1,3145 @@
+/*
+ * Copyright(c) 2006 - 2007 Atheros Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2008 Chris Snook <csnook@redhat.com>
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ */
+
+#include <linux/atomic.h>
+#include <linux/crc32.h>
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/hardirq.h>
+#include <linux/if_vlan.h>
+#include <linux/in.h>
+#include <linux/interrupt.h>
+#include <linux/ip.h>
+#include <linux/irqflags.h>
+#include <linux/irqreturn.h>
+#include <linux/mii.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/pm.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/tcp.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include "atl2.h"
+
+#define ATL2_DRV_VERSION "2.2.3"
+
+static const char atl2_driver_name[] = "atl2";
+static const char atl2_driver_string[] = "Atheros(R) L2 Ethernet Driver";
+static const char atl2_copyright[] = "Copyright (c) 2007 Atheros Corporation.";
+static const char atl2_driver_version[] = ATL2_DRV_VERSION;
+
+MODULE_AUTHOR("Atheros Corporation <xiong.huang@atheros.com>, Chris Snook <csnook@redhat.com>");
+MODULE_DESCRIPTION("Atheros Fast Ethernet Network Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(ATL2_DRV_VERSION);
+
+/*
+ * atl2_pci_tbl - PCI Device ID Table
+ */
+static DEFINE_PCI_DEVICE_TABLE(atl2_pci_tbl) = {
+ {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L2)},
+ /* required last entry */
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, atl2_pci_tbl);
+
+static void atl2_set_ethtool_ops(struct net_device *netdev);
+
+static void atl2_check_options(struct atl2_adapter *adapter);
+
+/*
+ * atl2_sw_init - Initialize general software structures (struct atl2_adapter)
+ * @adapter: board private structure to initialize
+ *
+ * atl2_sw_init initializes the Adapter private data structure.
+ * Fields are initialized based on PCI device information and
+ * OS network device settings (MTU size).
+ */
+static int __devinit atl2_sw_init(struct atl2_adapter *adapter)
+{
+ struct atl2_hw *hw = &adapter->hw;
+ struct pci_dev *pdev = adapter->pdev;
+
+ /* PCI config space info */
+ hw->vendor_id = pdev->vendor;
+ hw->device_id = pdev->device;
+ hw->subsystem_vendor_id = pdev->subsystem_vendor;
+ hw->subsystem_id = pdev->subsystem_device;
+ hw->revision_id = pdev->revision;
+
+ pci_read_config_word(pdev, PCI_COMMAND, &hw->pci_cmd_word);
+
+ adapter->wol = 0;
+ adapter->ict = 50000; /* ~100ms */
+ adapter->link_speed = SPEED_0; /* hardware init */
+ adapter->link_duplex = FULL_DUPLEX;
+
+ hw->phy_configured = false;
+ hw->preamble_len = 7;
+ hw->ipgt = 0x60;
+ hw->min_ifg = 0x50;
+ hw->ipgr1 = 0x40;
+ hw->ipgr2 = 0x60;
+ hw->retry_buf = 2;
+ hw->max_retry = 0xf;
+ hw->lcol = 0x37;
+ hw->jam_ipg = 7;
+ hw->fc_rxd_hi = 0;
+ hw->fc_rxd_lo = 0;
+ hw->max_frame_size = adapter->netdev->mtu;
+
+ spin_lock_init(&adapter->stats_lock);
+
+ set_bit(__ATL2_DOWN, &adapter->flags);
+
+ return 0;
+}
+
+/*
+ * atl2_set_multi - Multicast and Promiscuous mode set
+ * @netdev: network interface device structure
+ *
+ * The set_multi entry point is called whenever the multicast address
+ * list or the network interface flags are updated. This routine is
+ * responsible for configuring the hardware for proper multicast,
+ * promiscuous mode, and all-multi behavior.
+ */
+static void atl2_set_multi(struct net_device *netdev)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+ struct atl2_hw *hw = &adapter->hw;
+ struct netdev_hw_addr *ha;
+ u32 rctl;
+ u32 hash_value;
+
+ /* Check for Promiscuous and All Multicast modes */
+ rctl = ATL2_READ_REG(hw, REG_MAC_CTRL);
+
+ if (netdev->flags & IFF_PROMISC) {
+ rctl |= MAC_CTRL_PROMIS_EN;
+ } else if (netdev->flags & IFF_ALLMULTI) {
+ rctl |= MAC_CTRL_MC_ALL_EN;
+ rctl &= ~MAC_CTRL_PROMIS_EN;
+ } else
+ rctl &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN);
+
+ ATL2_WRITE_REG(hw, REG_MAC_CTRL, rctl);
+
+ /* clear the old settings from the multicast hash table */
+ ATL2_WRITE_REG(hw, REG_RX_HASH_TABLE, 0);
+ ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0);
+
+ /* comoute mc addresses' hash value ,and put it into hash table */
+ netdev_for_each_mc_addr(ha, netdev) {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ hash_value = atl2_hash_mc_addr(hw, ha->addr);
+#else
+ hash_value = atl2_hash_mc_addr(hw, ha->dmi_addr);
+#endif
+ atl2_hash_set(hw, hash_value);
+ }
+}
+
+static void init_ring_ptrs(struct atl2_adapter *adapter)
+{
+ /* Read / Write Ptr Initialize: */
+ adapter->txd_write_ptr = 0;
+ atomic_set(&adapter->txd_read_ptr, 0);
+
+ adapter->rxd_read_ptr = 0;
+ adapter->rxd_write_ptr = 0;
+
+ atomic_set(&adapter->txs_write_ptr, 0);
+ adapter->txs_next_clear = 0;
+}
+
+/*
+ * atl2_configure - Configure Transmit&Receive Unit after Reset
+ * @adapter: board private structure
+ *
+ * Configure the Tx /Rx unit of the MAC after a reset.
+ */
+static int atl2_configure(struct atl2_adapter *adapter)
+{
+ struct atl2_hw *hw = &adapter->hw;
+ u32 value;
+
+ /* clear interrupt status */
+ ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0xffffffff);
+
+ /* set MAC Address */
+ value = (((u32)hw->mac_addr[2]) << 24) |
+ (((u32)hw->mac_addr[3]) << 16) |
+ (((u32)hw->mac_addr[4]) << 8) |
+ (((u32)hw->mac_addr[5]));
+ ATL2_WRITE_REG(hw, REG_MAC_STA_ADDR, value);
+ value = (((u32)hw->mac_addr[0]) << 8) |
+ (((u32)hw->mac_addr[1]));
+ ATL2_WRITE_REG(hw, (REG_MAC_STA_ADDR+4), value);
+
+ /* HI base address */
+ ATL2_WRITE_REG(hw, REG_DESC_BASE_ADDR_HI,
+ (u32)((adapter->ring_dma & 0xffffffff00000000ULL) >> 32));
+
+ /* LO base address */
+ ATL2_WRITE_REG(hw, REG_TXD_BASE_ADDR_LO,
+ (u32)(adapter->txd_dma & 0x00000000ffffffffULL));
+ ATL2_WRITE_REG(hw, REG_TXS_BASE_ADDR_LO,
+ (u32)(adapter->txs_dma & 0x00000000ffffffffULL));
+ ATL2_WRITE_REG(hw, REG_RXD_BASE_ADDR_LO,
+ (u32)(adapter->rxd_dma & 0x00000000ffffffffULL));
+
+ /* element count */
+ ATL2_WRITE_REGW(hw, REG_TXD_MEM_SIZE, (u16)(adapter->txd_ring_size/4));
+ ATL2_WRITE_REGW(hw, REG_TXS_MEM_SIZE, (u16)adapter->txs_ring_size);
+ ATL2_WRITE_REGW(hw, REG_RXD_BUF_NUM, (u16)adapter->rxd_ring_size);
+
+ /* config Internal SRAM */
+/*
+ ATL2_WRITE_REGW(hw, REG_SRAM_TXRAM_END, sram_tx_end);
+ ATL2_WRITE_REGW(hw, REG_SRAM_TXRAM_END, sram_rx_end);
+*/
+
+ /* config IPG/IFG */
+ value = (((u32)hw->ipgt & MAC_IPG_IFG_IPGT_MASK) <<
+ MAC_IPG_IFG_IPGT_SHIFT) |
+ (((u32)hw->min_ifg & MAC_IPG_IFG_MIFG_MASK) <<
+ MAC_IPG_IFG_MIFG_SHIFT) |
+ (((u32)hw->ipgr1 & MAC_IPG_IFG_IPGR1_MASK) <<
+ MAC_IPG_IFG_IPGR1_SHIFT)|
+ (((u32)hw->ipgr2 & MAC_IPG_IFG_IPGR2_MASK) <<
+ MAC_IPG_IFG_IPGR2_SHIFT);
+ ATL2_WRITE_REG(hw, REG_MAC_IPG_IFG, value);
+
+ /* config Half-Duplex Control */
+ value = ((u32)hw->lcol & MAC_HALF_DUPLX_CTRL_LCOL_MASK) |
+ (((u32)hw->max_retry & MAC_HALF_DUPLX_CTRL_RETRY_MASK) <<
+ MAC_HALF_DUPLX_CTRL_RETRY_SHIFT) |
+ MAC_HALF_DUPLX_CTRL_EXC_DEF_EN |
+ (0xa << MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT) |
+ (((u32)hw->jam_ipg & MAC_HALF_DUPLX_CTRL_JAMIPG_MASK) <<
+ MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT);
+ ATL2_WRITE_REG(hw, REG_MAC_HALF_DUPLX_CTRL, value);
+
+ /* set Interrupt Moderator Timer */
+ ATL2_WRITE_REGW(hw, REG_IRQ_MODU_TIMER_INIT, adapter->imt);
+ ATL2_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_ITIMER_EN);
+
+ /* set Interrupt Clear Timer */
+ ATL2_WRITE_REGW(hw, REG_CMBDISDMA_TIMER, adapter->ict);
+
+ /* set MTU */
+ ATL2_WRITE_REG(hw, REG_MTU, adapter->netdev->mtu +
+ ENET_HEADER_SIZE + VLAN_SIZE + ETHERNET_FCS_SIZE);
+
+ /* 1590 */
+ ATL2_WRITE_REG(hw, REG_TX_CUT_THRESH, 0x177);
+
+ /* flow control */
+ ATL2_WRITE_REGW(hw, REG_PAUSE_ON_TH, hw->fc_rxd_hi);
+ ATL2_WRITE_REGW(hw, REG_PAUSE_OFF_TH, hw->fc_rxd_lo);
+
+ /* Init mailbox */
+ ATL2_WRITE_REGW(hw, REG_MB_TXD_WR_IDX, (u16)adapter->txd_write_ptr);
+ ATL2_WRITE_REGW(hw, REG_MB_RXD_RD_IDX, (u16)adapter->rxd_read_ptr);
+
+ /* enable DMA read/write */
+ ATL2_WRITE_REGB(hw, REG_DMAR, DMAR_EN);
+ ATL2_WRITE_REGB(hw, REG_DMAW, DMAW_EN);
+
+ value = ATL2_READ_REG(&adapter->hw, REG_ISR);
+ if ((value & ISR_PHY_LINKDOWN) != 0)
+ value = 1; /* config failed */
+ else
+ value = 0;
+
+ /* clear all interrupt status */
+ ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0x3fffffff);
+ ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0);
+ return value;
+}
+
+/*
+ * atl2_setup_ring_resources - allocate Tx / RX descriptor resources
+ * @adapter: board private structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static s32 atl2_setup_ring_resources(struct atl2_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ int size;
+ u8 offset = 0;
+
+ /* real ring DMA buffer */
+ adapter->ring_size = size =
+ adapter->txd_ring_size * 1 + 7 + /* dword align */
+ adapter->txs_ring_size * 4 + 7 + /* dword align */
+ adapter->rxd_ring_size * 1536 + 127; /* 128bytes align */
+
+ adapter->ring_vir_addr = pci_alloc_consistent(pdev, size,
+ &adapter->ring_dma);
+ if (!adapter->ring_vir_addr)
+ return -ENOMEM;
+ memset(adapter->ring_vir_addr, 0, adapter->ring_size);
+
+ /* Init TXD Ring */
+ adapter->txd_dma = adapter->ring_dma ;
+ offset = (adapter->txd_dma & 0x7) ? (8 - (adapter->txd_dma & 0x7)) : 0;
+ adapter->txd_dma += offset;
+ adapter->txd_ring = adapter->ring_vir_addr + offset;
+
+ /* Init TXS Ring */
+ adapter->txs_dma = adapter->txd_dma + adapter->txd_ring_size;
+ offset = (adapter->txs_dma & 0x7) ? (8 - (adapter->txs_dma & 0x7)) : 0;
+ adapter->txs_dma += offset;
+ adapter->txs_ring = (struct tx_pkt_status *)
+ (((u8 *)adapter->txd_ring) + (adapter->txd_ring_size + offset));
+
+ /* Init RXD Ring */
+ adapter->rxd_dma = adapter->txs_dma + adapter->txs_ring_size * 4;
+ offset = (adapter->rxd_dma & 127) ?
+ (128 - (adapter->rxd_dma & 127)) : 0;
+ if (offset > 7)
+ offset -= 8;
+ else
+ offset += (128 - 8);
+
+ adapter->rxd_dma += offset;
+ adapter->rxd_ring = (struct rx_desc *) (((u8 *)adapter->txs_ring) +
+ (adapter->txs_ring_size * 4 + offset));
+
+/*
+ * Read / Write Ptr Initialize:
+ * init_ring_ptrs(adapter);
+ */
+ return 0;
+}
+
+/*
+ * atl2_irq_enable - Enable default interrupt generation settings
+ * @adapter: board private structure
+ */
+static inline void atl2_irq_enable(struct atl2_adapter *adapter)
+{
+ ATL2_WRITE_REG(&adapter->hw, REG_IMR, IMR_NORMAL_MASK);
+ ATL2_WRITE_FLUSH(&adapter->hw);
+}
+
+/*
+ * atl2_irq_disable - Mask off interrupt generation on the NIC
+ * @adapter: board private structure
+ */
+static inline void atl2_irq_disable(struct atl2_adapter *adapter)
+{
+ ATL2_WRITE_REG(&adapter->hw, REG_IMR, 0);
+ ATL2_WRITE_FLUSH(&adapter->hw);
+ synchronize_irq(adapter->pdev->irq);
+}
+
+static void __atl2_vlan_mode(u32 features, u32 *ctrl)
+{
+ if (features & NETIF_F_HW_VLAN_RX) {
+ /* enable VLAN tag insert/strip */
+ *ctrl |= MAC_CTRL_RMV_VLAN;
+ } else {
+ /* disable VLAN tag insert/strip */
+ *ctrl &= ~MAC_CTRL_RMV_VLAN;
+ }
+}
+
+static void atl2_vlan_mode(struct net_device *netdev, u32 features)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+ u32 ctrl;
+
+ atl2_irq_disable(adapter);
+
+ ctrl = ATL2_READ_REG(&adapter->hw, REG_MAC_CTRL);
+ __atl2_vlan_mode(features, &ctrl);
+ ATL2_WRITE_REG(&adapter->hw, REG_MAC_CTRL, ctrl);
+
+ atl2_irq_enable(adapter);
+}
+
+static void atl2_restore_vlan(struct atl2_adapter *adapter)
+{
+ atl2_vlan_mode(adapter->netdev, adapter->netdev->features);
+}
+
+static u32 atl2_fix_features(struct net_device *netdev, u32 features)
+{
+ /*
+ * Since there is no support for separate rx/tx vlan accel
+ * enable/disable make sure tx flag is always in same state as rx.
+ */
+ if (features & NETIF_F_HW_VLAN_RX)
+ features |= NETIF_F_HW_VLAN_TX;
+ else
+ features &= ~NETIF_F_HW_VLAN_TX;
+
+ return features;
+}
+
+static int atl2_set_features(struct net_device *netdev, u32 features)
+{
+ u32 changed = netdev->features ^ features;
+
+ if (changed & NETIF_F_HW_VLAN_RX)
+ atl2_vlan_mode(netdev, features);
+
+ return 0;
+}
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */
+
+static void atl2_intr_rx(struct atl2_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ struct rx_desc *rxd;
+ struct sk_buff *skb;
+
+ do {
+ rxd = adapter->rxd_ring+adapter->rxd_write_ptr;
+ if (!rxd->status.update)
+ break; /* end of tx */
+
+ /* clear this flag at once */
+ rxd->status.update = 0;
+
+ if (rxd->status.ok && rxd->status.pkt_size >= 60) {
+ int rx_size = (int)(rxd->status.pkt_size - 4);
+ /* alloc new buffer */
+ skb = netdev_alloc_skb_ip_align(netdev, rx_size);
+ if (NULL == skb) {
+ printk(KERN_WARNING
+ "%s: Mem squeeze, deferring packet.\n",
+ netdev->name);
+ /*
+ * Check that some rx space is free. If not,
+ * free one and mark stats->rx_dropped++.
+ */
+ netdev->stats.rx_dropped++;
+ break;
+ }
+ memcpy(skb->data, rxd->packet, rx_size);
+ skb_put(skb, rx_size);
+ skb->protocol = eth_type_trans(skb, netdev);
+ if (rxd->status.vlan) {
+ u16 vlan_tag = (rxd->status.vtag>>4) |
+ ((rxd->status.vtag&7) << 13) |
+ ((rxd->status.vtag&8) << 9);
+
+ __vlan_hwaccel_put_tag(skb, vlan_tag);
+ }
+ netif_rx(skb);
+ netdev->stats.rx_bytes += rx_size;
+ netdev->stats.rx_packets++;
+ } else {
+ netdev->stats.rx_errors++;
+
+ if (rxd->status.ok && rxd->status.pkt_size <= 60)
+ netdev->stats.rx_length_errors++;
+ if (rxd->status.mcast)
+ netdev->stats.multicast++;
+ if (rxd->status.crc)
+ netdev->stats.rx_crc_errors++;
+ if (rxd->status.align)
+ netdev->stats.rx_frame_errors++;
+ }
+
+ /* advance write ptr */
+ if (++adapter->rxd_write_ptr == adapter->rxd_ring_size)
+ adapter->rxd_write_ptr = 0;
+ } while (1);
+
+ /* update mailbox? */
+ adapter->rxd_read_ptr = adapter->rxd_write_ptr;
+ ATL2_WRITE_REGW(&adapter->hw, REG_MB_RXD_RD_IDX, adapter->rxd_read_ptr);
+}
+
+static void atl2_intr_tx(struct atl2_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ u32 txd_read_ptr;
+ u32 txs_write_ptr;
+ struct tx_pkt_status *txs;
+ struct tx_pkt_header *txph;
+ int free_hole = 0;
+
+ do {
+ txs_write_ptr = (u32) atomic_read(&adapter->txs_write_ptr);
+ txs = adapter->txs_ring + txs_write_ptr;
+ if (!txs->update)
+ break; /* tx stop here */
+
+ free_hole = 1;
+ txs->update = 0;
+
+ if (++txs_write_ptr == adapter->txs_ring_size)
+ txs_write_ptr = 0;
+ atomic_set(&adapter->txs_write_ptr, (int)txs_write_ptr);
+
+ txd_read_ptr = (u32) atomic_read(&adapter->txd_read_ptr);
+ txph = (struct tx_pkt_header *)
+ (((u8 *)adapter->txd_ring) + txd_read_ptr);
+
+ if (txph->pkt_size != txs->pkt_size) {
+ struct tx_pkt_status *old_txs = txs;
+ printk(KERN_WARNING
+ "%s: txs packet size not consistent with txd"
+ " txd_:0x%08x, txs_:0x%08x!\n",
+ adapter->netdev->name,
+ *(u32 *)txph, *(u32 *)txs);
+ printk(KERN_WARNING
+ "txd read ptr: 0x%x\n",
+ txd_read_ptr);
+ txs = adapter->txs_ring + txs_write_ptr;
+ printk(KERN_WARNING
+ "txs-behind:0x%08x\n",
+ *(u32 *)txs);
+ if (txs_write_ptr < 2) {
+ txs = adapter->txs_ring +
+ (adapter->txs_ring_size +
+ txs_write_ptr - 2);
+ } else {
+ txs = adapter->txs_ring + (txs_write_ptr - 2);
+ }
+ printk(KERN_WARNING
+ "txs-before:0x%08x\n",
+ *(u32 *)txs);
+ txs = old_txs;
+ }
+
+ /* 4for TPH */
+ txd_read_ptr += (((u32)(txph->pkt_size) + 7) & ~3);
+ if (txd_read_ptr >= adapter->txd_ring_size)
+ txd_read_ptr -= adapter->txd_ring_size;
+
+ atomic_set(&adapter->txd_read_ptr, (int)txd_read_ptr);
+
+ /* tx statistics: */
+ if (txs->ok) {
+ netdev->stats.tx_bytes += txs->pkt_size;
+ netdev->stats.tx_packets++;
+ }
+ else
+ netdev->stats.tx_errors++;
+
+ if (txs->defer)
+ netdev->stats.collisions++;
+ if (txs->abort_col)
+ netdev->stats.tx_aborted_errors++;
+ if (txs->late_col)
+ netdev->stats.tx_window_errors++;
+ if (txs->underun)
+ netdev->stats.tx_fifo_errors++;
+ } while (1);
+
+ if (free_hole) {
+ if (netif_queue_stopped(adapter->netdev) &&
+ netif_carrier_ok(adapter->netdev))
+ netif_wake_queue(adapter->netdev);
+ }
+}
+
+static void atl2_check_for_link(struct atl2_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ u16 phy_data = 0;
+
+ spin_lock(&adapter->stats_lock);
+ atl2_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
+ atl2_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
+ spin_unlock(&adapter->stats_lock);
+
+ /* notify upper layer link down ASAP */
+ if (!(phy_data & BMSR_LSTATUS)) { /* Link Down */
+ if (netif_carrier_ok(netdev)) { /* old link state: Up */
+ printk(KERN_INFO "%s: %s NIC Link is Down\n",
+ atl2_driver_name, netdev->name);
+ adapter->link_speed = SPEED_0;
+ netif_carrier_off(netdev);
+ netif_stop_queue(netdev);
+ }
+ }
+ schedule_work(&adapter->link_chg_task);
+}
+
+static inline void atl2_clear_phy_int(struct atl2_adapter *adapter)
+{
+ u16 phy_data;
+ spin_lock(&adapter->stats_lock);
+ atl2_read_phy_reg(&adapter->hw, 19, &phy_data);
+ spin_unlock(&adapter->stats_lock);
+}
+
+/*
+ * atl2_intr - Interrupt Handler
+ * @irq: interrupt number
+ * @data: pointer to a network interface device structure
+ * @pt_regs: CPU registers structure
+ */
+static irqreturn_t atl2_intr(int irq, void *data)
+{
+ struct atl2_adapter *adapter = netdev_priv(data);
+ struct atl2_hw *hw = &adapter->hw;
+ u32 status;
+
+ status = ATL2_READ_REG(hw, REG_ISR);
+ if (0 == status)
+ return IRQ_NONE;
+
+ /* link event */
+ if (status & ISR_PHY)
+ atl2_clear_phy_int(adapter);
+
+ /* clear ISR status, and Enable CMB DMA/Disable Interrupt */
+ ATL2_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT);
+
+ /* check if PCIE PHY Link down */
+ if (status & ISR_PHY_LINKDOWN) {
+ if (netif_running(adapter->netdev)) { /* reset MAC */
+ ATL2_WRITE_REG(hw, REG_ISR, 0);
+ ATL2_WRITE_REG(hw, REG_IMR, 0);
+ ATL2_WRITE_FLUSH(hw);
+ schedule_work(&adapter->reset_task);
+ return IRQ_HANDLED;
+ }
+ }
+
+ /* check if DMA read/write error? */
+ if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) {
+ ATL2_WRITE_REG(hw, REG_ISR, 0);
+ ATL2_WRITE_REG(hw, REG_IMR, 0);
+ ATL2_WRITE_FLUSH(hw);
+ schedule_work(&adapter->reset_task);
+ return IRQ_HANDLED;
+ }
+
+ /* link event */
+ if (status & (ISR_PHY | ISR_MANUAL)) {
+ adapter->netdev->stats.tx_carrier_errors++;
+ atl2_check_for_link(adapter);
+ }
+
+ /* transmit event */
+ if (status & ISR_TX_EVENT)
+ atl2_intr_tx(adapter);
+
+ /* rx exception */
+ if (status & ISR_RX_EVENT)
+ atl2_intr_rx(adapter);
+
+ /* re-enable Interrupt */
+ ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0);
+ return IRQ_HANDLED;
+}
+
+static int atl2_request_irq(struct atl2_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ int flags, err = 0;
+
+ flags = IRQF_SHARED;
+ adapter->have_msi = true;
+ err = pci_enable_msi(adapter->pdev);
+ if (err)
+ adapter->have_msi = false;
+
+ if (adapter->have_msi)
+ flags &= ~IRQF_SHARED;
+
+ return request_irq(adapter->pdev->irq, atl2_intr, flags, netdev->name,
+ netdev);
+}
+
+/*
+ * atl2_free_ring_resources - Free Tx / RX descriptor Resources
+ * @adapter: board private structure
+ *
+ * Free all transmit software resources
+ */
+static void atl2_free_ring_resources(struct atl2_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ pci_free_consistent(pdev, adapter->ring_size, adapter->ring_vir_addr,
+ adapter->ring_dma);
+}
+
+/*
+ * atl2_open - Called when a network interface is made active
+ * @netdev: network interface device structure
+ *
+ * Returns 0 on success, negative value on failure
+ *
+ * The open entry point is called when a network interface is made
+ * active by the system (IFF_UP). At this point all resources needed
+ * for transmit and receive operations are allocated, the interrupt
+ * handler is registered with the OS, the watchdog timer is started,
+ * and the stack is notified that the interface is ready.
+ */
+static int atl2_open(struct net_device *netdev)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+ int err;
+ u32 val;
+
+ /* disallow open during test */
+ if (test_bit(__ATL2_TESTING, &adapter->flags))
+ return -EBUSY;
+
+ /* allocate transmit descriptors */
+ err = atl2_setup_ring_resources(adapter);
+ if (err)
+ return err;
+
+ err = atl2_init_hw(&adapter->hw);
+ if (err) {
+ err = -EIO;
+ goto err_init_hw;
+ }
+
+ /* hardware has been reset, we need to reload some things */
+ atl2_set_multi(netdev);
+ init_ring_ptrs(adapter);
+
+ atl2_restore_vlan(adapter);
+
+ if (atl2_configure(adapter)) {
+ err = -EIO;
+ goto err_config;
+ }
+
+ err = atl2_request_irq(adapter);
+ if (err)
+ goto err_req_irq;
+
+ clear_bit(__ATL2_DOWN, &adapter->flags);
+
+ mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + 4*HZ));
+
+ val = ATL2_READ_REG(&adapter->hw, REG_MASTER_CTRL);
+ ATL2_WRITE_REG(&adapter->hw, REG_MASTER_CTRL,
+ val | MASTER_CTRL_MANUAL_INT);
+
+ atl2_irq_enable(adapter);
+
+ return 0;
+
+err_init_hw:
+err_req_irq:
+err_config:
+ atl2_free_ring_resources(adapter);
+ atl2_reset_hw(&adapter->hw);
+
+ return err;
+}
+
+static void atl2_down(struct atl2_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+
+ /* signal that we're down so the interrupt handler does not
+ * reschedule our watchdog timer */
+ set_bit(__ATL2_DOWN, &adapter->flags);
+
+ netif_tx_disable(netdev);
+
+ /* reset MAC to disable all RX/TX */
+ atl2_reset_hw(&adapter->hw);
+ msleep(1);
+
+ atl2_irq_disable(adapter);
+
+ del_timer_sync(&adapter->watchdog_timer);
+ del_timer_sync(&adapter->phy_config_timer);
+ clear_bit(0, &adapter->cfg_phy);
+
+ netif_carrier_off(netdev);
+ adapter->link_speed = SPEED_0;
+ adapter->link_duplex = -1;
+}
+
+static void atl2_free_irq(struct atl2_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+
+ free_irq(adapter->pdev->irq, netdev);
+
+#ifdef CONFIG_PCI_MSI
+ if (adapter->have_msi)
+ pci_disable_msi(adapter->pdev);
+#endif
+}
+
+/*
+ * atl2_close - Disables a network interface
+ * @netdev: network interface device structure
+ *
+ * Returns 0, this is not allowed to fail
+ *
+ * The close entry point is called when an interface is de-activated
+ * by the OS. The hardware is still under the drivers control, but
+ * needs to be disabled. A global MAC reset is issued to stop the
+ * hardware, and all transmit and receive resources are freed.
+ */
+static int atl2_close(struct net_device *netdev)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+
+ WARN_ON(test_bit(__ATL2_RESETTING, &adapter->flags));
+
+ atl2_down(adapter);
+ atl2_free_irq(adapter);
+ atl2_free_ring_resources(adapter);
+
+ return 0;
+}
+
+static inline int TxsFreeUnit(struct atl2_adapter *adapter)
+{
+ u32 txs_write_ptr = (u32) atomic_read(&adapter->txs_write_ptr);
+
+ return (adapter->txs_next_clear >= txs_write_ptr) ?
+ (int) (adapter->txs_ring_size - adapter->txs_next_clear +
+ txs_write_ptr - 1) :
+ (int) (txs_write_ptr - adapter->txs_next_clear - 1);
+}
+
+static inline int TxdFreeBytes(struct atl2_adapter *adapter)
+{
+ u32 txd_read_ptr = (u32)atomic_read(&adapter->txd_read_ptr);
+
+ return (adapter->txd_write_ptr >= txd_read_ptr) ?
+ (int) (adapter->txd_ring_size - adapter->txd_write_ptr +
+ txd_read_ptr - 1) :
+ (int) (txd_read_ptr - adapter->txd_write_ptr - 1);
+}
+
+static netdev_tx_t atl2_xmit_frame(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+ struct tx_pkt_header *txph;
+ u32 offset, copy_len;
+ int txs_unused;
+ int txbuf_unused;
+
+ if (test_bit(__ATL2_DOWN, &adapter->flags)) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ if (unlikely(skb->len <= 0)) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ txs_unused = TxsFreeUnit(adapter);
+ txbuf_unused = TxdFreeBytes(adapter);
+
+ if (skb->len + sizeof(struct tx_pkt_header) + 4 > txbuf_unused ||
+ txs_unused < 1) {
+ /* not enough resources */
+ netif_stop_queue(netdev);
+ return NETDEV_TX_BUSY;
+ }
+
+ offset = adapter->txd_write_ptr;
+
+ txph = (struct tx_pkt_header *) (((u8 *)adapter->txd_ring) + offset);
+
+ *(u32 *)txph = 0;
+ txph->pkt_size = skb->len;
+
+ offset += 4;
+ if (offset >= adapter->txd_ring_size)
+ offset -= adapter->txd_ring_size;
+ copy_len = adapter->txd_ring_size - offset;
+ if (copy_len >= skb->len) {
+ memcpy(((u8 *)adapter->txd_ring) + offset, skb->data, skb->len);
+ offset += ((u32)(skb->len + 3) & ~3);
+ } else {
+ memcpy(((u8 *)adapter->txd_ring)+offset, skb->data, copy_len);
+ memcpy((u8 *)adapter->txd_ring, skb->data+copy_len,
+ skb->len-copy_len);
+ offset = ((u32)(skb->len-copy_len + 3) & ~3);
+ }
+#ifdef NETIF_F_HW_VLAN_TX
+ if (vlan_tx_tag_present(skb)) {
+ u16 vlan_tag = vlan_tx_tag_get(skb);
+ vlan_tag = (vlan_tag << 4) |
+ (vlan_tag >> 13) |
+ ((vlan_tag >> 9) & 0x8);
+ txph->ins_vlan = 1;
+ txph->vlan = vlan_tag;
+ }
+#endif
+ if (offset >= adapter->txd_ring_size)
+ offset -= adapter->txd_ring_size;
+ adapter->txd_write_ptr = offset;
+
+ /* clear txs before send */
+ adapter->txs_ring[adapter->txs_next_clear].update = 0;
+ if (++adapter->txs_next_clear == adapter->txs_ring_size)
+ adapter->txs_next_clear = 0;
+
+ ATL2_WRITE_REGW(&adapter->hw, REG_MB_TXD_WR_IDX,
+ (adapter->txd_write_ptr >> 2));
+
+ mmiowb();
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+}
+
+/*
+ * atl2_change_mtu - Change the Maximum Transfer Unit
+ * @netdev: network interface device structure
+ * @new_mtu: new value for maximum frame size
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int atl2_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+ struct atl2_hw *hw = &adapter->hw;
+
+ if ((new_mtu < 40) || (new_mtu > (ETH_DATA_LEN + VLAN_SIZE)))
+ return -EINVAL;
+
+ /* set MTU */
+ if (hw->max_frame_size != new_mtu) {
+ netdev->mtu = new_mtu;
+ ATL2_WRITE_REG(hw, REG_MTU, new_mtu + ENET_HEADER_SIZE +
+ VLAN_SIZE + ETHERNET_FCS_SIZE);
+ }
+
+ return 0;
+}
+
+/*
+ * atl2_set_mac - Change the Ethernet Address of the NIC
+ * @netdev: network interface device structure
+ * @p: pointer to an address structure
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int atl2_set_mac(struct net_device *netdev, void *p)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+ struct sockaddr *addr = p;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ if (netif_running(netdev))
+ return -EBUSY;
+
+ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+ memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len);
+
+ atl2_set_mac_addr(&adapter->hw);
+
+ return 0;
+}
+
+/*
+ * atl2_mii_ioctl -
+ * @netdev:
+ * @ifreq:
+ * @cmd:
+ */
+static int atl2_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+ struct mii_ioctl_data *data = if_mii(ifr);
+ unsigned long flags;
+
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ data->phy_id = 0;
+ break;
+ case SIOCGMIIREG:
+ spin_lock_irqsave(&adapter->stats_lock, flags);
+ if (atl2_read_phy_reg(&adapter->hw,
+ data->reg_num & 0x1F, &data->val_out)) {
+ spin_unlock_irqrestore(&adapter->stats_lock, flags);
+ return -EIO;
+ }
+ spin_unlock_irqrestore(&adapter->stats_lock, flags);
+ break;
+ case SIOCSMIIREG:
+ if (data->reg_num & ~(0x1F))
+ return -EFAULT;
+ spin_lock_irqsave(&adapter->stats_lock, flags);
+ if (atl2_write_phy_reg(&adapter->hw, data->reg_num,
+ data->val_in)) {
+ spin_unlock_irqrestore(&adapter->stats_lock, flags);
+ return -EIO;
+ }
+ spin_unlock_irqrestore(&adapter->stats_lock, flags);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/*
+ * atl2_ioctl -
+ * @netdev:
+ * @ifreq:
+ * @cmd:
+ */
+static int atl2_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ case SIOCGMIIREG:
+ case SIOCSMIIREG:
+ return atl2_mii_ioctl(netdev, ifr, cmd);
+#ifdef ETHTOOL_OPS_COMPAT
+ case SIOCETHTOOL:
+ return ethtool_ioctl(ifr);
+#endif
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/*
+ * atl2_tx_timeout - Respond to a Tx Hang
+ * @netdev: network interface device structure
+ */
+static void atl2_tx_timeout(struct net_device *netdev)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+
+ /* Do the reset outside of interrupt context */
+ schedule_work(&adapter->reset_task);
+}
+
+/*
+ * atl2_watchdog - Timer Call-back
+ * @data: pointer to netdev cast into an unsigned long
+ */
+static void atl2_watchdog(unsigned long data)
+{
+ struct atl2_adapter *adapter = (struct atl2_adapter *) data;
+
+ if (!test_bit(__ATL2_DOWN, &adapter->flags)) {
+ u32 drop_rxd, drop_rxs;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->stats_lock, flags);
+ drop_rxd = ATL2_READ_REG(&adapter->hw, REG_STS_RXD_OV);
+ drop_rxs = ATL2_READ_REG(&adapter->hw, REG_STS_RXS_OV);
+ spin_unlock_irqrestore(&adapter->stats_lock, flags);
+
+ adapter->netdev->stats.rx_over_errors += drop_rxd + drop_rxs;
+
+ /* Reset the timer */
+ mod_timer(&adapter->watchdog_timer,
+ round_jiffies(jiffies + 4 * HZ));
+ }
+}
+
+/*
+ * atl2_phy_config - Timer Call-back
+ * @data: pointer to netdev cast into an unsigned long
+ */
+static void atl2_phy_config(unsigned long data)
+{
+ struct atl2_adapter *adapter = (struct atl2_adapter *) data;
+ struct atl2_hw *hw = &adapter->hw;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->stats_lock, flags);
+ atl2_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg);
+ atl2_write_phy_reg(hw, MII_BMCR, MII_CR_RESET | MII_CR_AUTO_NEG_EN |
+ MII_CR_RESTART_AUTO_NEG);
+ spin_unlock_irqrestore(&adapter->stats_lock, flags);
+ clear_bit(0, &adapter->cfg_phy);
+}
+
+static int atl2_up(struct atl2_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ int err = 0;
+ u32 val;
+
+ /* hardware has been reset, we need to reload some things */
+
+ err = atl2_init_hw(&adapter->hw);
+ if (err) {
+ err = -EIO;
+ return err;
+ }
+
+ atl2_set_multi(netdev);
+ init_ring_ptrs(adapter);
+
+ atl2_restore_vlan(adapter);
+
+ if (atl2_configure(adapter)) {
+ err = -EIO;
+ goto err_up;
+ }
+
+ clear_bit(__ATL2_DOWN, &adapter->flags);
+
+ val = ATL2_READ_REG(&adapter->hw, REG_MASTER_CTRL);
+ ATL2_WRITE_REG(&adapter->hw, REG_MASTER_CTRL, val |
+ MASTER_CTRL_MANUAL_INT);
+
+ atl2_irq_enable(adapter);
+
+err_up:
+ return err;
+}
+
+static void atl2_reinit_locked(struct atl2_adapter *adapter)
+{
+ WARN_ON(in_interrupt());
+ while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags))
+ msleep(1);
+ atl2_down(adapter);
+ atl2_up(adapter);
+ clear_bit(__ATL2_RESETTING, &adapter->flags);
+}
+
+static void atl2_reset_task(struct work_struct *work)
+{
+ struct atl2_adapter *adapter;
+ adapter = container_of(work, struct atl2_adapter, reset_task);
+
+ atl2_reinit_locked(adapter);
+}
+
+static void atl2_setup_mac_ctrl(struct atl2_adapter *adapter)
+{
+ u32 value;
+ struct atl2_hw *hw = &adapter->hw;
+ struct net_device *netdev = adapter->netdev;
+
+ /* Config MAC CTRL Register */
+ value = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN | MAC_CTRL_MACLP_CLK_PHY;
+
+ /* duplex */
+ if (FULL_DUPLEX == adapter->link_duplex)
+ value |= MAC_CTRL_DUPLX;
+
+ /* flow control */
+ value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW);
+
+ /* PAD & CRC */
+ value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD);
+
+ /* preamble length */
+ value |= (((u32)adapter->hw.preamble_len & MAC_CTRL_PRMLEN_MASK) <<
+ MAC_CTRL_PRMLEN_SHIFT);
+
+ /* vlan */
+ __atl2_vlan_mode(netdev->features, &value);
+
+ /* filter mode */
+ value |= MAC_CTRL_BC_EN;
+ if (netdev->flags & IFF_PROMISC)
+ value |= MAC_CTRL_PROMIS_EN;
+ else if (netdev->flags & IFF_ALLMULTI)
+ value |= MAC_CTRL_MC_ALL_EN;
+
+ /* half retry buffer */
+ value |= (((u32)(adapter->hw.retry_buf &
+ MAC_CTRL_HALF_LEFT_BUF_MASK)) << MAC_CTRL_HALF_LEFT_BUF_SHIFT);
+
+ ATL2_WRITE_REG(hw, REG_MAC_CTRL, value);
+}
+
+static int atl2_check_link(struct atl2_adapter *adapter)
+{
+ struct atl2_hw *hw = &adapter->hw;
+ struct net_device *netdev = adapter->netdev;
+ int ret_val;
+ u16 speed, duplex, phy_data;
+ int reconfig = 0;
+
+ /* MII_BMSR must read twise */
+ atl2_read_phy_reg(hw, MII_BMSR, &phy_data);
+ atl2_read_phy_reg(hw, MII_BMSR, &phy_data);
+ if (!(phy_data&BMSR_LSTATUS)) { /* link down */
+ if (netif_carrier_ok(netdev)) { /* old link state: Up */
+ u32 value;
+ /* disable rx */
+ value = ATL2_READ_REG(hw, REG_MAC_CTRL);
+ value &= ~MAC_CTRL_RX_EN;
+ ATL2_WRITE_REG(hw, REG_MAC_CTRL, value);
+ adapter->link_speed = SPEED_0;
+ netif_carrier_off(netdev);
+ netif_stop_queue(netdev);
+ }
+ return 0;
+ }
+
+ /* Link Up */
+ ret_val = atl2_get_speed_and_duplex(hw, &speed, &duplex);
+ if (ret_val)
+ return ret_val;
+ switch (hw->MediaType) {
+ case MEDIA_TYPE_100M_FULL:
+ if (speed != SPEED_100 || duplex != FULL_DUPLEX)
+ reconfig = 1;
+ break;
+ case MEDIA_TYPE_100M_HALF:
+ if (speed != SPEED_100 || duplex != HALF_DUPLEX)
+ reconfig = 1;
+ break;
+ case MEDIA_TYPE_10M_FULL:
+ if (speed != SPEED_10 || duplex != FULL_DUPLEX)
+ reconfig = 1;
+ break;
+ case MEDIA_TYPE_10M_HALF:
+ if (speed != SPEED_10 || duplex != HALF_DUPLEX)
+ reconfig = 1;
+ break;
+ }
+ /* link result is our setting */
+ if (reconfig == 0) {
+ if (adapter->link_speed != speed ||
+ adapter->link_duplex != duplex) {
+ adapter->link_speed = speed;
+ adapter->link_duplex = duplex;
+ atl2_setup_mac_ctrl(adapter);
+ printk(KERN_INFO "%s: %s NIC Link is Up<%d Mbps %s>\n",
+ atl2_driver_name, netdev->name,
+ adapter->link_speed,
+ adapter->link_duplex == FULL_DUPLEX ?
+ "Full Duplex" : "Half Duplex");
+ }
+
+ if (!netif_carrier_ok(netdev)) { /* Link down -> Up */
+ netif_carrier_on(netdev);
+ netif_wake_queue(netdev);
+ }
+ return 0;
+ }
+
+ /* change original link status */
+ if (netif_carrier_ok(netdev)) {
+ u32 value;
+ /* disable rx */
+ value = ATL2_READ_REG(hw, REG_MAC_CTRL);
+ value &= ~MAC_CTRL_RX_EN;
+ ATL2_WRITE_REG(hw, REG_MAC_CTRL, value);
+
+ adapter->link_speed = SPEED_0;
+ netif_carrier_off(netdev);
+ netif_stop_queue(netdev);
+ }
+
+ /* auto-neg, insert timer to re-config phy
+ * (if interval smaller than 5 seconds, something strange) */
+ if (!test_bit(__ATL2_DOWN, &adapter->flags)) {
+ if (!test_and_set_bit(0, &adapter->cfg_phy))
+ mod_timer(&adapter->phy_config_timer,
+ round_jiffies(jiffies + 5 * HZ));
+ }
+
+ return 0;
+}
+
+/*
+ * atl2_link_chg_task - deal with link change event Out of interrupt context
+ * @netdev: network interface device structure
+ */
+static void atl2_link_chg_task(struct work_struct *work)
+{
+ struct atl2_adapter *adapter;
+ unsigned long flags;
+
+ adapter = container_of(work, struct atl2_adapter, link_chg_task);
+
+ spin_lock_irqsave(&adapter->stats_lock, flags);
+ atl2_check_link(adapter);
+ spin_unlock_irqrestore(&adapter->stats_lock, flags);
+}
+
+static void atl2_setup_pcicmd(struct pci_dev *pdev)
+{
+ u16 cmd;
+
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+
+ if (cmd & PCI_COMMAND_INTX_DISABLE)
+ cmd &= ~PCI_COMMAND_INTX_DISABLE;
+ if (cmd & PCI_COMMAND_IO)
+ cmd &= ~PCI_COMMAND_IO;
+ if (0 == (cmd & PCI_COMMAND_MEMORY))
+ cmd |= PCI_COMMAND_MEMORY;
+ if (0 == (cmd & PCI_COMMAND_MASTER))
+ cmd |= PCI_COMMAND_MASTER;
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
+
+ /*
+ * some motherboards BIOS(PXE/EFI) driver may set PME
+ * while they transfer control to OS (Windows/Linux)
+ * so we should clear this bit before NIC work normally
+ */
+ pci_write_config_dword(pdev, REG_PM_CTRLSTAT, 0);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void atl2_poll_controller(struct net_device *netdev)
+{
+ disable_irq(netdev->irq);
+ atl2_intr(netdev->irq, netdev);
+ enable_irq(netdev->irq);
+}
+#endif
+
+
+static const struct net_device_ops atl2_netdev_ops = {
+ .ndo_open = atl2_open,
+ .ndo_stop = atl2_close,
+ .ndo_start_xmit = atl2_xmit_frame,
+ .ndo_set_rx_mode = atl2_set_multi,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = atl2_set_mac,
+ .ndo_change_mtu = atl2_change_mtu,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39))
+ .ndo_fix_features = atl2_fix_features,
+ .ndo_set_features = atl2_set_features,
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */
+ .ndo_do_ioctl = atl2_ioctl,
+ .ndo_tx_timeout = atl2_tx_timeout,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = atl2_poll_controller,
+#endif
+};
+
+/*
+ * atl2_probe - Device Initialization Routine
+ * @pdev: PCI device information struct
+ * @ent: entry in atl2_pci_tbl
+ *
+ * Returns 0 on success, negative on failure
+ *
+ * atl2_probe initializes an adapter identified by a pci_dev structure.
+ * The OS initialization, configuring of the adapter private structure,
+ * and a hardware reset occur.
+ */
+static int __devinit atl2_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *netdev;
+ struct atl2_adapter *adapter;
+ static int cards_found;
+ unsigned long mmio_start;
+ int mmio_len;
+ int err;
+
+ cards_found = 0;
+
+ err = pci_enable_device(pdev);
+ if (err)
+ return err;
+
+ /*
+ * atl2 is a shared-high-32-bit device, so we're stuck with 32-bit DMA
+ * until the kernel has the proper infrastructure to support 64-bit DMA
+ * on these devices.
+ */
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) &&
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ printk(KERN_ERR "atl2: No usable DMA configuration, aborting\n");
+ goto err_dma;
+ }
+
+ /* Mark all PCI regions associated with PCI device
+ * pdev as being reserved by owner atl2_driver_name */
+ err = pci_request_regions(pdev, atl2_driver_name);
+ if (err)
+ goto err_pci_reg;
+
+ /* Enables bus-mastering on the device and calls
+ * pcibios_set_master to do the needed arch specific settings */
+ pci_set_master(pdev);
+
+ err = -ENOMEM;
+ netdev = alloc_etherdev(sizeof(struct atl2_adapter));
+ if (!netdev)
+ goto err_alloc_etherdev;
+
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+
+ pci_set_drvdata(pdev, netdev);
+ adapter = netdev_priv(netdev);
+ adapter->netdev = netdev;
+ adapter->pdev = pdev;
+ adapter->hw.back = adapter;
+
+ mmio_start = pci_resource_start(pdev, 0x0);
+ mmio_len = pci_resource_len(pdev, 0x0);
+
+ adapter->hw.mem_rang = (u32)mmio_len;
+ adapter->hw.hw_addr = ioremap(mmio_start, mmio_len);
+ if (!adapter->hw.hw_addr) {
+ err = -EIO;
+ goto err_ioremap;
+ }
+
+ atl2_setup_pcicmd(pdev);
+
+ netdev_attach_ops(netdev, &atl2_netdev_ops);
+ atl2_set_ethtool_ops(netdev);
+ netdev->watchdog_timeo = 5 * HZ;
+ strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
+
+ netdev->mem_start = mmio_start;
+ netdev->mem_end = mmio_start + mmio_len;
+ adapter->bd_number = cards_found;
+ adapter->pci_using_64 = false;
+
+ /* setup the private structure */
+ err = atl2_sw_init(adapter);
+ if (err)
+ goto err_sw_init;
+
+ err = -EIO;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39))
+ netdev->hw_features = NETIF_F_SG | NETIF_F_HW_VLAN_RX;
+#endif
+#if defined(NETIF_F_HW_VLAN_TX) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39))
+ netdev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX);
+#endif
+
+ /* Init PHY as early as possible due to power saving issue */
+ atl2_phy_init(&adapter->hw);
+
+ /* reset the controller to
+ * put the device in a known good starting state */
+
+ if (atl2_reset_hw(&adapter->hw)) {
+ err = -EIO;
+ goto err_reset;
+ }
+
+ /* copy the MAC address out of the EEPROM */
+ atl2_read_mac_addr(&adapter->hw);
+ memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len);
+/* FIXME: do we still need this? */
+#ifdef ETHTOOL_GPERMADDR
+ memcpy(netdev->perm_addr, adapter->hw.mac_addr, netdev->addr_len);
+
+ if (!is_valid_ether_addr(netdev->perm_addr)) {
+#else
+ if (!is_valid_ether_addr(netdev->dev_addr)) {
+#endif
+ err = -EIO;
+ goto err_eeprom;
+ }
+
+ atl2_check_options(adapter);
+
+ init_timer(&adapter->watchdog_timer);
+ adapter->watchdog_timer.function = atl2_watchdog;
+ adapter->watchdog_timer.data = (unsigned long) adapter;
+
+ init_timer(&adapter->phy_config_timer);
+ adapter->phy_config_timer.function = atl2_phy_config;
+ adapter->phy_config_timer.data = (unsigned long) adapter;
+
+ INIT_WORK(&adapter->reset_task, atl2_reset_task);
+ INIT_WORK(&adapter->link_chg_task, atl2_link_chg_task);
+
+ strcpy(netdev->name, "eth%d"); /* ?? */
+ err = register_netdev(netdev);
+ if (err)
+ goto err_register;
+
+ /* assume we have no link for now */
+ netif_carrier_off(netdev);
+ netif_stop_queue(netdev);
+
+ cards_found++;
+
+ return 0;
+
+err_reset:
+err_register:
+err_sw_init:
+err_eeprom:
+ iounmap(adapter->hw.hw_addr);
+err_ioremap:
+ free_netdev(netdev);
+err_alloc_etherdev:
+ pci_release_regions(pdev);
+err_pci_reg:
+err_dma:
+ pci_disable_device(pdev);
+ return err;
+}
+
+/*
+ * atl2_remove - Device Removal Routine
+ * @pdev: PCI device information struct
+ *
+ * atl2_remove is called by the PCI subsystem to alert the driver
+ * that it should release a PCI device. The could be caused by a
+ * Hot-Plug event, or because the driver is going to be removed from
+ * memory.
+ */
+/* FIXME: write the original MAC address back in case it was changed from a
+ * BIOS-set value, as in atl1 -- CHS */
+static void __devexit atl2_remove(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+
+ /* flush_scheduled work may reschedule our watchdog task, so
+ * explicitly disable watchdog tasks from being rescheduled */
+ set_bit(__ATL2_DOWN, &adapter->flags);
+
+ del_timer_sync(&adapter->watchdog_timer);
+ del_timer_sync(&adapter->phy_config_timer);
+ cancel_work_sync(&adapter->reset_task);
+ cancel_work_sync(&adapter->link_chg_task);
+
+ unregister_netdev(netdev);
+
+ atl2_force_ps(&adapter->hw);
+
+ iounmap(adapter->hw.hw_addr);
+ pci_release_regions(pdev);
+
+ free_netdev(netdev);
+
+ pci_disable_device(pdev);
+}
+
+static int atl2_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+ struct atl2_hw *hw = &adapter->hw;
+ u16 speed, duplex;
+ u32 ctrl = 0;
+ u32 wufc = adapter->wol;
+
+#ifdef CONFIG_PM
+ int retval = 0;
+#endif
+
+ netif_device_detach(netdev);
+
+ if (netif_running(netdev)) {
+ WARN_ON(test_bit(__ATL2_RESETTING, &adapter->flags));
+ atl2_down(adapter);
+ }
+
+#ifdef CONFIG_PM
+ retval = pci_save_state(pdev);
+ if (retval)
+ return retval;
+#endif
+
+ atl2_read_phy_reg(hw, MII_BMSR, (u16 *)&ctrl);
+ atl2_read_phy_reg(hw, MII_BMSR, (u16 *)&ctrl);
+ if (ctrl & BMSR_LSTATUS)
+ wufc &= ~ATLX_WUFC_LNKC;
+
+ if (0 != (ctrl & BMSR_LSTATUS) && 0 != wufc) {
+ u32 ret_val;
+ /* get current link speed & duplex */
+ ret_val = atl2_get_speed_and_duplex(hw, &speed, &duplex);
+ if (ret_val) {
+ printk(KERN_DEBUG
+ "%s: get speed&duplex error while suspend\n",
+ atl2_driver_name);
+ goto wol_dis;
+ }
+
+ ctrl = 0;
+
+ /* turn on magic packet wol */
+ if (wufc & ATLX_WUFC_MAG)
+ ctrl |= (WOL_MAGIC_EN | WOL_MAGIC_PME_EN);
+
+ /* ignore Link Chg event when Link is up */
+ ATL2_WRITE_REG(hw, REG_WOL_CTRL, ctrl);
+
+ /* Config MAC CTRL Register */
+ ctrl = MAC_CTRL_RX_EN | MAC_CTRL_MACLP_CLK_PHY;
+ if (FULL_DUPLEX == adapter->link_duplex)
+ ctrl |= MAC_CTRL_DUPLX;
+ ctrl |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD);
+ ctrl |= (((u32)adapter->hw.preamble_len &
+ MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT);
+ ctrl |= (((u32)(adapter->hw.retry_buf &
+ MAC_CTRL_HALF_LEFT_BUF_MASK)) <<
+ MAC_CTRL_HALF_LEFT_BUF_SHIFT);
+ if (wufc & ATLX_WUFC_MAG) {
+ /* magic packet maybe Broadcast&multicast&Unicast */
+ ctrl |= MAC_CTRL_BC_EN;
+ }
+
+ ATL2_WRITE_REG(hw, REG_MAC_CTRL, ctrl);
+
+ /* pcie patch */
+ ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC);
+ ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
+ ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl);
+ ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1);
+ ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK;
+ ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl);
+
+ pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
+ goto suspend_exit;
+ }
+
+ if (0 == (ctrl&BMSR_LSTATUS) && 0 != (wufc&ATLX_WUFC_LNKC)) {
+ /* link is down, so only LINK CHG WOL event enable */
+ ctrl |= (WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN);
+ ATL2_WRITE_REG(hw, REG_WOL_CTRL, ctrl);
+ ATL2_WRITE_REG(hw, REG_MAC_CTRL, 0);
+
+ /* pcie patch */
+ ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC);
+ ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
+ ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl);
+ ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1);
+ ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK;
+ ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl);
+
+ hw->phy_configured = false; /* re-init PHY when resume */
+
+ pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
+
+ goto suspend_exit;
+ }
+
+wol_dis:
+ /* WOL disabled */
+ ATL2_WRITE_REG(hw, REG_WOL_CTRL, 0);
+
+ /* pcie patch */
+ ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC);
+ ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
+ ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl);
+ ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1);
+ ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK;
+ ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl);
+
+ atl2_force_ps(hw);
+ hw->phy_configured = false; /* re-init PHY when resume */
+
+ pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
+
+suspend_exit:
+ if (netif_running(netdev))
+ atl2_free_irq(adapter);
+
+ pci_disable_device(pdev);
+
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int atl2_resume(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+ u32 err;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ printk(KERN_ERR
+ "atl2: Cannot enable PCI device from suspend\n");
+ return err;
+ }
+
+ pci_set_master(pdev);
+
+ ATL2_READ_REG(&adapter->hw, REG_WOL_CTRL); /* clear WOL status */
+
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ pci_enable_wake(pdev, PCI_D3cold, 0);
+
+ ATL2_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0);
+
+ if (netif_running(netdev)) {
+ err = atl2_request_irq(adapter);
+ if (err)
+ return err;
+ }
+
+ atl2_reset_hw(&adapter->hw);
+
+ if (netif_running(netdev))
+ atl2_up(adapter);
+
+ netif_device_attach(netdev);
+
+ return 0;
+}
+#endif
+
+static void atl2_shutdown(struct pci_dev *pdev)
+{
+ atl2_suspend(pdev, PMSG_SUSPEND);
+}
+
+static struct pci_driver atl2_driver = {
+ .name = atl2_driver_name,
+ .id_table = atl2_pci_tbl,
+ .probe = atl2_probe,
+ .remove = __devexit_p(atl2_remove),
+ /* Power Management Hooks */
+ .suspend = atl2_suspend,
+#ifdef CONFIG_PM
+ .resume = atl2_resume,
+#endif
+ .shutdown = atl2_shutdown,
+};
+
+/*
+ * atl2_init_module - Driver Registration Routine
+ *
+ * atl2_init_module is the first routine called when the driver is
+ * loaded. All it does is register with the PCI subsystem.
+ */
+static int __init atl2_init_module(void)
+{
+ printk(KERN_INFO "%s - version %s\n", atl2_driver_string,
+ atl2_driver_version);
+ printk(KERN_INFO "%s\n", atl2_copyright);
+ return pci_register_driver(&atl2_driver);
+}
+module_init(atl2_init_module);
+
+/*
+ * atl2_exit_module - Driver Exit Cleanup Routine
+ *
+ * atl2_exit_module is called just before the driver is removed
+ * from memory.
+ */
+static void __exit atl2_exit_module(void)
+{
+ pci_unregister_driver(&atl2_driver);
+}
+module_exit(atl2_exit_module);
+
+static void atl2_read_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value)
+{
+ struct atl2_adapter *adapter = hw->back;
+ pci_read_config_word(adapter->pdev, reg, value);
+}
+
+static void atl2_write_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value)
+{
+ struct atl2_adapter *adapter = hw->back;
+ pci_write_config_word(adapter->pdev, reg, *value);
+}
+
+static int atl2_get_settings(struct net_device *netdev,
+ struct ethtool_cmd *ecmd)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+ struct atl2_hw *hw = &adapter->hw;
+
+ ecmd->supported = (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_Autoneg |
+ SUPPORTED_TP);
+ ecmd->advertising = ADVERTISED_TP;
+
+ ecmd->advertising |= ADVERTISED_Autoneg;
+ ecmd->advertising |= hw->autoneg_advertised;
+
+ ecmd->port = PORT_TP;
+ ecmd->phy_address = 0;
+ ecmd->transceiver = XCVR_INTERNAL;
+
+ if (adapter->link_speed != SPEED_0) {
+ ethtool_cmd_speed_set(ecmd, adapter->link_speed);
+ if (adapter->link_duplex == FULL_DUPLEX)
+ ecmd->duplex = DUPLEX_FULL;
+ else
+ ecmd->duplex = DUPLEX_HALF;
+ } else {
+ ethtool_cmd_speed_set(ecmd, -1);
+ ecmd->duplex = -1;
+ }
+
+ ecmd->autoneg = AUTONEG_ENABLE;
+ return 0;
+}
+
+static int atl2_set_settings(struct net_device *netdev,
+ struct ethtool_cmd *ecmd)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+ struct atl2_hw *hw = &adapter->hw;
+
+ while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags))
+ msleep(1);
+
+ if (ecmd->autoneg == AUTONEG_ENABLE) {
+#define MY_ADV_MASK (ADVERTISE_10_HALF | \
+ ADVERTISE_10_FULL | \
+ ADVERTISE_100_HALF| \
+ ADVERTISE_100_FULL)
+
+ if ((ecmd->advertising & MY_ADV_MASK) == MY_ADV_MASK) {
+ hw->MediaType = MEDIA_TYPE_AUTO_SENSOR;
+ hw->autoneg_advertised = MY_ADV_MASK;
+ } else if ((ecmd->advertising & MY_ADV_MASK) ==
+ ADVERTISE_100_FULL) {
+ hw->MediaType = MEDIA_TYPE_100M_FULL;
+ hw->autoneg_advertised = ADVERTISE_100_FULL;
+ } else if ((ecmd->advertising & MY_ADV_MASK) ==
+ ADVERTISE_100_HALF) {
+ hw->MediaType = MEDIA_TYPE_100M_HALF;
+ hw->autoneg_advertised = ADVERTISE_100_HALF;
+ } else if ((ecmd->advertising & MY_ADV_MASK) ==
+ ADVERTISE_10_FULL) {
+ hw->MediaType = MEDIA_TYPE_10M_FULL;
+ hw->autoneg_advertised = ADVERTISE_10_FULL;
+ } else if ((ecmd->advertising & MY_ADV_MASK) ==
+ ADVERTISE_10_HALF) {
+ hw->MediaType = MEDIA_TYPE_10M_HALF;
+ hw->autoneg_advertised = ADVERTISE_10_HALF;
+ } else {
+ clear_bit(__ATL2_RESETTING, &adapter->flags);
+ return -EINVAL;
+ }
+ ecmd->advertising = hw->autoneg_advertised |
+ ADVERTISED_TP | ADVERTISED_Autoneg;
+ } else {
+ clear_bit(__ATL2_RESETTING, &adapter->flags);
+ return -EINVAL;
+ }
+
+ /* reset the link */
+ if (netif_running(adapter->netdev)) {
+ atl2_down(adapter);
+ atl2_up(adapter);
+ } else
+ atl2_reset_hw(&adapter->hw);
+
+ clear_bit(__ATL2_RESETTING, &adapter->flags);
+ return 0;
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39))
+static u32 atl2_get_tx_csum(struct net_device *netdev)
+{
+ return (netdev->features & NETIF_F_HW_CSUM) != 0;
+}
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */
+
+static u32 atl2_get_msglevel(struct net_device *netdev)
+{
+ return 0;
+}
+
+/*
+ * It's sane for this to be empty, but we might want to take advantage of this.
+ */
+static void atl2_set_msglevel(struct net_device *netdev, u32 data)
+{
+}
+
+static int atl2_get_regs_len(struct net_device *netdev)
+{
+#define ATL2_REGS_LEN 42
+ return sizeof(u32) * ATL2_REGS_LEN;
+}
+
+static void atl2_get_regs(struct net_device *netdev,
+ struct ethtool_regs *regs, void *p)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+ struct atl2_hw *hw = &adapter->hw;
+ u32 *regs_buff = p;
+ u16 phy_data;
+
+ memset(p, 0, sizeof(u32) * ATL2_REGS_LEN);
+
+ regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id;
+
+ regs_buff[0] = ATL2_READ_REG(hw, REG_VPD_CAP);
+ regs_buff[1] = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL);
+ regs_buff[2] = ATL2_READ_REG(hw, REG_SPI_FLASH_CONFIG);
+ regs_buff[3] = ATL2_READ_REG(hw, REG_TWSI_CTRL);
+ regs_buff[4] = ATL2_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL);
+ regs_buff[5] = ATL2_READ_REG(hw, REG_MASTER_CTRL);
+ regs_buff[6] = ATL2_READ_REG(hw, REG_MANUAL_TIMER_INIT);
+ regs_buff[7] = ATL2_READ_REG(hw, REG_IRQ_MODU_TIMER_INIT);
+ regs_buff[8] = ATL2_READ_REG(hw, REG_PHY_ENABLE);
+ regs_buff[9] = ATL2_READ_REG(hw, REG_CMBDISDMA_TIMER);
+ regs_buff[10] = ATL2_READ_REG(hw, REG_IDLE_STATUS);
+ regs_buff[11] = ATL2_READ_REG(hw, REG_MDIO_CTRL);
+ regs_buff[12] = ATL2_READ_REG(hw, REG_SERDES_LOCK);
+ regs_buff[13] = ATL2_READ_REG(hw, REG_MAC_CTRL);
+ regs_buff[14] = ATL2_READ_REG(hw, REG_MAC_IPG_IFG);
+ regs_buff[15] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR);
+ regs_buff[16] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR+4);
+ regs_buff[17] = ATL2_READ_REG(hw, REG_RX_HASH_TABLE);
+ regs_buff[18] = ATL2_READ_REG(hw, REG_RX_HASH_TABLE+4);
+ regs_buff[19] = ATL2_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL);
+ regs_buff[20] = ATL2_READ_REG(hw, REG_MTU);
+ regs_buff[21] = ATL2_READ_REG(hw, REG_WOL_CTRL);
+ regs_buff[22] = ATL2_READ_REG(hw, REG_SRAM_TXRAM_END);
+ regs_buff[23] = ATL2_READ_REG(hw, REG_DESC_BASE_ADDR_HI);
+ regs_buff[24] = ATL2_READ_REG(hw, REG_TXD_BASE_ADDR_LO);
+ regs_buff[25] = ATL2_READ_REG(hw, REG_TXD_MEM_SIZE);
+ regs_buff[26] = ATL2_READ_REG(hw, REG_TXS_BASE_ADDR_LO);
+ regs_buff[27] = ATL2_READ_REG(hw, REG_TXS_MEM_SIZE);
+ regs_buff[28] = ATL2_READ_REG(hw, REG_RXD_BASE_ADDR_LO);
+ regs_buff[29] = ATL2_READ_REG(hw, REG_RXD_BUF_NUM);
+ regs_buff[30] = ATL2_READ_REG(hw, REG_DMAR);
+ regs_buff[31] = ATL2_READ_REG(hw, REG_TX_CUT_THRESH);
+ regs_buff[32] = ATL2_READ_REG(hw, REG_DMAW);
+ regs_buff[33] = ATL2_READ_REG(hw, REG_PAUSE_ON_TH);
+ regs_buff[34] = ATL2_READ_REG(hw, REG_PAUSE_OFF_TH);
+ regs_buff[35] = ATL2_READ_REG(hw, REG_MB_TXD_WR_IDX);
+ regs_buff[36] = ATL2_READ_REG(hw, REG_MB_RXD_RD_IDX);
+ regs_buff[38] = ATL2_READ_REG(hw, REG_ISR);
+ regs_buff[39] = ATL2_READ_REG(hw, REG_IMR);
+
+ atl2_read_phy_reg(hw, MII_BMCR, &phy_data);
+ regs_buff[40] = (u32)phy_data;
+ atl2_read_phy_reg(hw, MII_BMSR, &phy_data);
+ regs_buff[41] = (u32)phy_data;
+}
+
+static int atl2_get_eeprom_len(struct net_device *netdev)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+
+ if (!atl2_check_eeprom_exist(&adapter->hw))
+ return 512;
+ else
+ return 0;
+}
+
+static int atl2_get_eeprom(struct net_device *netdev,
+ struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+ struct atl2_hw *hw = &adapter->hw;
+ u32 *eeprom_buff;
+ int first_dword, last_dword;
+ int ret_val = 0;
+ int i;
+
+ if (eeprom->len == 0)
+ return -EINVAL;
+
+ if (atl2_check_eeprom_exist(hw))
+ return -EINVAL;
+
+ eeprom->magic = hw->vendor_id | (hw->device_id << 16);
+
+ first_dword = eeprom->offset >> 2;
+ last_dword = (eeprom->offset + eeprom->len - 1) >> 2;
+
+ eeprom_buff = kmalloc(sizeof(u32) * (last_dword - first_dword + 1),
+ GFP_KERNEL);
+ if (!eeprom_buff)
+ return -ENOMEM;
+
+ for (i = first_dword; i < last_dword; i++) {
+ if (!atl2_read_eeprom(hw, i*4, &(eeprom_buff[i-first_dword]))) {
+ ret_val = -EIO;
+ goto free;
+ }
+ }
+
+ memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3),
+ eeprom->len);
+free:
+ kfree(eeprom_buff);
+
+ return ret_val;
+}
+
+static int atl2_set_eeprom(struct net_device *netdev,
+ struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+ struct atl2_hw *hw = &adapter->hw;
+ u32 *eeprom_buff;
+ u32 *ptr;
+ int max_len, first_dword, last_dword, ret_val = 0;
+ int i;
+
+ if (eeprom->len == 0)
+ return -EOPNOTSUPP;
+
+ if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16)))
+ return -EFAULT;
+
+ max_len = 512;
+
+ first_dword = eeprom->offset >> 2;
+ last_dword = (eeprom->offset + eeprom->len - 1) >> 2;
+ eeprom_buff = kmalloc(max_len, GFP_KERNEL);
+ if (!eeprom_buff)
+ return -ENOMEM;
+
+ ptr = eeprom_buff;
+
+ if (eeprom->offset & 3) {
+ /* need read/modify/write of first changed EEPROM word */
+ /* only the second byte of the word is being modified */
+ if (!atl2_read_eeprom(hw, first_dword*4, &(eeprom_buff[0]))) {
+ ret_val = -EIO;
+ goto out;
+ }
+ ptr++;
+ }
+ if (((eeprom->offset + eeprom->len) & 3)) {
+ /*
+ * need read/modify/write of last changed EEPROM word
+ * only the first byte of the word is being modified
+ */
+ if (!atl2_read_eeprom(hw, last_dword * 4,
+ &(eeprom_buff[last_dword - first_dword]))) {
+ ret_val = -EIO;
+ goto out;
+ }
+ }
+
+ /* Device's eeprom is always little-endian, word addressable */
+ memcpy(ptr, bytes, eeprom->len);
+
+ for (i = 0; i < last_dword - first_dword + 1; i++) {
+ if (!atl2_write_eeprom(hw, ((first_dword+i)*4), eeprom_buff[i])) {
+ ret_val = -EIO;
+ goto out;
+ }
+ }
+ out:
+ kfree(eeprom_buff);
+ return ret_val;
+}
+
+static void atl2_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+
+ strncpy(drvinfo->driver, atl2_driver_name, 32);
+ strncpy(drvinfo->version, atl2_driver_version, 32);
+ strncpy(drvinfo->fw_version, "L2", 32);
+ strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);
+ drvinfo->n_stats = 0;
+ drvinfo->testinfo_len = 0;
+ drvinfo->regdump_len = atl2_get_regs_len(netdev);
+ drvinfo->eedump_len = atl2_get_eeprom_len(netdev);
+}
+
+static void atl2_get_wol(struct net_device *netdev,
+ struct ethtool_wolinfo *wol)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+
+ wol->supported = WAKE_MAGIC;
+ wol->wolopts = 0;
+
+ if (adapter->wol & ATLX_WUFC_EX)
+ wol->wolopts |= WAKE_UCAST;
+ if (adapter->wol & ATLX_WUFC_MC)
+ wol->wolopts |= WAKE_MCAST;
+ if (adapter->wol & ATLX_WUFC_BC)
+ wol->wolopts |= WAKE_BCAST;
+ if (adapter->wol & ATLX_WUFC_MAG)
+ wol->wolopts |= WAKE_MAGIC;
+ if (adapter->wol & ATLX_WUFC_LNKC)
+ wol->wolopts |= WAKE_PHY;
+}
+
+static int atl2_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+
+ if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE))
+ return -EOPNOTSUPP;
+
+ if (wol->wolopts & (WAKE_UCAST | WAKE_BCAST | WAKE_MCAST))
+ return -EOPNOTSUPP;
+
+ /* these settings will always override what we currently have */
+ adapter->wol = 0;
+
+ if (wol->wolopts & WAKE_MAGIC)
+ adapter->wol |= ATLX_WUFC_MAG;
+ if (wol->wolopts & WAKE_PHY)
+ adapter->wol |= ATLX_WUFC_LNKC;
+
+ return 0;
+}
+
+static int atl2_nway_reset(struct net_device *netdev)
+{
+ struct atl2_adapter *adapter = netdev_priv(netdev);
+ if (netif_running(netdev))
+ atl2_reinit_locked(adapter);
+ return 0;
+}
+
+static const struct ethtool_ops atl2_ethtool_ops = {
+ .get_settings = atl2_get_settings,
+ .set_settings = atl2_set_settings,
+ .get_drvinfo = atl2_get_drvinfo,
+ .get_regs_len = atl2_get_regs_len,
+ .get_regs = atl2_get_regs,
+ .get_wol = atl2_get_wol,
+ .set_wol = atl2_set_wol,
+ .get_msglevel = atl2_get_msglevel,
+ .set_msglevel = atl2_set_msglevel,
+ .nway_reset = atl2_nway_reset,
+ .get_link = ethtool_op_get_link,
+ .get_eeprom_len = atl2_get_eeprom_len,
+ .get_eeprom = atl2_get_eeprom,
+ .set_eeprom = atl2_set_eeprom,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39))
+ .get_tx_csum = atl2_get_tx_csum,
+ .get_sg = ethtool_op_get_sg,
+ .set_sg = ethtool_op_set_sg,
+#ifdef NETIF_F_TSO
+ .get_tso = ethtool_op_get_tso,
+#endif
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */
+};
+
+static void atl2_set_ethtool_ops(struct net_device *netdev)
+{
+ SET_ETHTOOL_OPS(netdev, &atl2_ethtool_ops);
+}
+
+#define LBYTESWAP(a) ((((a) & 0x00ff00ff) << 8) | \
+ (((a) & 0xff00ff00) >> 8))
+#define LONGSWAP(a) ((LBYTESWAP(a) << 16) | (LBYTESWAP(a) >> 16))
+#define SHORTSWAP(a) (((a) << 8) | ((a) >> 8))
+
+/*
+ * Reset the transmit and receive units; mask and clear all interrupts.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * return : 0 or idle status (if error)
+ */
+static s32 atl2_reset_hw(struct atl2_hw *hw)
+{
+ u32 icr;
+ u16 pci_cfg_cmd_word;
+ int i;
+
+ /* Workaround for PCI problem when BIOS sets MMRBC incorrectly. */
+ atl2_read_pci_cfg(hw, PCI_REG_COMMAND, &pci_cfg_cmd_word);
+ if ((pci_cfg_cmd_word &
+ (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER)) !=
+ (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER)) {
+ pci_cfg_cmd_word |=
+ (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER);
+ atl2_write_pci_cfg(hw, PCI_REG_COMMAND, &pci_cfg_cmd_word);
+ }
+
+ /* Clear Interrupt mask to stop board from generating
+ * interrupts & Clear any pending interrupt events
+ */
+ /* FIXME */
+ /* ATL2_WRITE_REG(hw, REG_IMR, 0); */
+ /* ATL2_WRITE_REG(hw, REG_ISR, 0xffffffff); */
+
+ /* Issue Soft Reset to the MAC. This will reset the chip's
+ * transmit, receive, DMA. It will not effect
+ * the current PCI configuration. The global reset bit is self-
+ * clearing, and should clear within a microsecond.
+ */
+ ATL2_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_SOFT_RST);
+ wmb();
+ msleep(1); /* delay about 1ms */
+
+ /* Wait at least 10ms for All module to be Idle */
+ for (i = 0; i < 10; i++) {
+ icr = ATL2_READ_REG(hw, REG_IDLE_STATUS);
+ if (!icr)
+ break;
+ msleep(1); /* delay 1 ms */
+ cpu_relax();
+ }
+
+ if (icr)
+ return icr;
+
+ return 0;
+}
+
+#define CUSTOM_SPI_CS_SETUP 2
+#define CUSTOM_SPI_CLK_HI 2
+#define CUSTOM_SPI_CLK_LO 2
+#define CUSTOM_SPI_CS_HOLD 2
+#define CUSTOM_SPI_CS_HI 3
+
+static struct atl2_spi_flash_dev flash_table[] =
+{
+/* MFR WRSR READ PROGRAM WREN WRDI RDSR RDID SECTOR_ERASE CHIP_ERASE */
+{"Atmel", 0x0, 0x03, 0x02, 0x06, 0x04, 0x05, 0x15, 0x52, 0x62 },
+{"SST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0x90, 0x20, 0x60 },
+{"ST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0xAB, 0xD8, 0xC7 },
+};
+
+static bool atl2_spi_read(struct atl2_hw *hw, u32 addr, u32 *buf)
+{
+ int i;
+ u32 value;
+
+ ATL2_WRITE_REG(hw, REG_SPI_DATA, 0);
+ ATL2_WRITE_REG(hw, REG_SPI_ADDR, addr);
+
+ value = SPI_FLASH_CTRL_WAIT_READY |
+ (CUSTOM_SPI_CS_SETUP & SPI_FLASH_CTRL_CS_SETUP_MASK) <<
+ SPI_FLASH_CTRL_CS_SETUP_SHIFT |
+ (CUSTOM_SPI_CLK_HI & SPI_FLASH_CTRL_CLK_HI_MASK) <<
+ SPI_FLASH_CTRL_CLK_HI_SHIFT |
+ (CUSTOM_SPI_CLK_LO & SPI_FLASH_CTRL_CLK_LO_MASK) <<
+ SPI_FLASH_CTRL_CLK_LO_SHIFT |
+ (CUSTOM_SPI_CS_HOLD & SPI_FLASH_CTRL_CS_HOLD_MASK) <<
+ SPI_FLASH_CTRL_CS_HOLD_SHIFT |
+ (CUSTOM_SPI_CS_HI & SPI_FLASH_CTRL_CS_HI_MASK) <<
+ SPI_FLASH_CTRL_CS_HI_SHIFT |
+ (0x1 & SPI_FLASH_CTRL_INS_MASK) << SPI_FLASH_CTRL_INS_SHIFT;
+
+ ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value);
+
+ value |= SPI_FLASH_CTRL_START;
+
+ ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value);
+
+ for (i = 0; i < 10; i++) {
+ msleep(1);
+ value = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL);
+ if (!(value & SPI_FLASH_CTRL_START))
+ break;
+ }
+
+ if (value & SPI_FLASH_CTRL_START)
+ return false;
+
+ *buf = ATL2_READ_REG(hw, REG_SPI_DATA);
+
+ return true;
+}
+
+/*
+ * get_permanent_address
+ * return 0 if get valid mac address,
+ */
+static int get_permanent_address(struct atl2_hw *hw)
+{
+ u32 Addr[2];
+ u32 i, Control;
+ u16 Register;
+ u8 EthAddr[NODE_ADDRESS_SIZE];
+ bool KeyValid;
+
+ if (is_valid_ether_addr(hw->perm_mac_addr))
+ return 0;
+
+ Addr[0] = 0;
+ Addr[1] = 0;
+
+ if (!atl2_check_eeprom_exist(hw)) { /* eeprom exists */
+ Register = 0;
+ KeyValid = false;
+
+ /* Read out all EEPROM content */
+ i = 0;
+ while (1) {
+ if (atl2_read_eeprom(hw, i + 0x100, &Control)) {
+ if (KeyValid) {
+ if (Register == REG_MAC_STA_ADDR)
+ Addr[0] = Control;
+ else if (Register ==
+ (REG_MAC_STA_ADDR + 4))
+ Addr[1] = Control;
+ KeyValid = false;
+ } else if ((Control & 0xff) == 0x5A) {
+ KeyValid = true;
+ Register = (u16) (Control >> 16);
+ } else {
+ /* assume data end while encount an invalid KEYWORD */
+ break;
+ }
+ } else {
+ break; /* read error */
+ }
+ i += 4;
+ }
+
+ *(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]);
+ *(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *) &Addr[1]);
+
+ if (is_valid_ether_addr(EthAddr)) {
+ memcpy(hw->perm_mac_addr, EthAddr, NODE_ADDRESS_SIZE);
+ return 0;
+ }
+ return 1;
+ }
+
+ /* see if SPI flash exists? */
+ Addr[0] = 0;
+ Addr[1] = 0;
+ Register = 0;
+ KeyValid = false;
+ i = 0;
+ while (1) {
+ if (atl2_spi_read(hw, i + 0x1f000, &Control)) {
+ if (KeyValid) {
+ if (Register == REG_MAC_STA_ADDR)
+ Addr[0] = Control;
+ else if (Register == (REG_MAC_STA_ADDR + 4))
+ Addr[1] = Control;
+ KeyValid = false;
+ } else if ((Control & 0xff) == 0x5A) {
+ KeyValid = true;
+ Register = (u16) (Control >> 16);
+ } else {
+ break; /* data end */
+ }
+ } else {
+ break; /* read error */
+ }
+ i += 4;
+ }
+
+ *(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]);
+ *(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *)&Addr[1]);
+ if (is_valid_ether_addr(EthAddr)) {
+ memcpy(hw->perm_mac_addr, EthAddr, NODE_ADDRESS_SIZE);
+ return 0;
+ }
+ /* maybe MAC-address is from BIOS */
+ Addr[0] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR);
+ Addr[1] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR + 4);
+ *(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]);
+ *(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *) &Addr[1]);
+
+ if (is_valid_ether_addr(EthAddr)) {
+ memcpy(hw->perm_mac_addr, EthAddr, NODE_ADDRESS_SIZE);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Reads the adapter's MAC address from the EEPROM
+ *
+ * hw - Struct containing variables accessed by shared code
+ */
+static s32 atl2_read_mac_addr(struct atl2_hw *hw)
+{
+ u16 i;
+
+ if (get_permanent_address(hw)) {
+ /* for test */
+ /* FIXME: shouldn't we use random_ether_addr() here? */
+ hw->perm_mac_addr[0] = 0x00;
+ hw->perm_mac_addr[1] = 0x13;
+ hw->perm_mac_addr[2] = 0x74;
+ hw->perm_mac_addr[3] = 0x00;
+ hw->perm_mac_addr[4] = 0x5c;
+ hw->perm_mac_addr[5] = 0x38;
+ }
+
+ for (i = 0; i < NODE_ADDRESS_SIZE; i++)
+ hw->mac_addr[i] = hw->perm_mac_addr[i];
+
+ return 0;
+}
+
+/*
+ * Hashes an address to determine its location in the multicast table
+ *
+ * hw - Struct containing variables accessed by shared code
+ * mc_addr - the multicast address to hash
+ *
+ * atl2_hash_mc_addr
+ * purpose
+ * set hash value for a multicast address
+ * hash calcu processing :
+ * 1. calcu 32bit CRC for multicast address
+ * 2. reverse crc with MSB to LSB
+ */
+static u32 atl2_hash_mc_addr(struct atl2_hw *hw, u8 *mc_addr)
+{
+ u32 crc32, value;
+ int i;
+
+ value = 0;
+ crc32 = ether_crc_le(6, mc_addr);
+
+ for (i = 0; i < 32; i++)
+ value |= (((crc32 >> i) & 1) << (31 - i));
+
+ return value;
+}
+
+/*
+ * Sets the bit in the multicast table corresponding to the hash value.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * hash_value - Multicast address hash value
+ */
+static void atl2_hash_set(struct atl2_hw *hw, u32 hash_value)
+{
+ u32 hash_bit, hash_reg;
+ u32 mta;
+
+ /* The HASH Table is a register array of 2 32-bit registers.
+ * It is treated like an array of 64 bits. We want to set
+ * bit BitArray[hash_value]. So we figure out what register
+ * the bit is in, read it, OR in the new bit, then write
+ * back the new value. The register is determined by the
+ * upper 7 bits of the hash value and the bit within that
+ * register are determined by the lower 5 bits of the value.
+ */
+ hash_reg = (hash_value >> 31) & 0x1;
+ hash_bit = (hash_value >> 26) & 0x1F;
+
+ mta = ATL2_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg);
+
+ mta |= (1 << hash_bit);
+
+ ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta);
+}
+
+/*
+ * atl2_init_pcie - init PCIE module
+ */
+static void atl2_init_pcie(struct atl2_hw *hw)
+{
+ u32 value;
+ value = LTSSM_TEST_MODE_DEF;
+ ATL2_WRITE_REG(hw, REG_LTSSM_TEST_MODE, value);
+
+ value = PCIE_DLL_TX_CTRL1_DEF;
+ ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, value);
+}
+
+static void atl2_init_flash_opcode(struct atl2_hw *hw)
+{
+ if (hw->flash_vendor >= ARRAY_SIZE(flash_table))
+ hw->flash_vendor = 0; /* ATMEL */
+
+ /* Init OP table */
+ ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_PROGRAM,
+ flash_table[hw->flash_vendor].cmdPROGRAM);
+ ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_SC_ERASE,
+ flash_table[hw->flash_vendor].cmdSECTOR_ERASE);
+ ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_CHIP_ERASE,
+ flash_table[hw->flash_vendor].cmdCHIP_ERASE);
+ ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_RDID,
+ flash_table[hw->flash_vendor].cmdRDID);
+ ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_WREN,
+ flash_table[hw->flash_vendor].cmdWREN);
+ ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_RDSR,
+ flash_table[hw->flash_vendor].cmdRDSR);
+ ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_WRSR,
+ flash_table[hw->flash_vendor].cmdWRSR);
+ ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_READ,
+ flash_table[hw->flash_vendor].cmdREAD);
+}
+
+/********************************************************************
+* Performs basic configuration of the adapter.
+*
+* hw - Struct containing variables accessed by shared code
+* Assumes that the controller has previously been reset and is in a
+* post-reset uninitialized state. Initializes multicast table,
+* and Calls routines to setup link
+* Leaves the transmit and receive units disabled and uninitialized.
+********************************************************************/
+static s32 atl2_init_hw(struct atl2_hw *hw)
+{
+ u32 ret_val = 0;
+
+ atl2_init_pcie(hw);
+
+ /* Zero out the Multicast HASH table */
+ /* clear the old settings from the multicast hash table */
+ ATL2_WRITE_REG(hw, REG_RX_HASH_TABLE, 0);
+ ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0);
+
+ atl2_init_flash_opcode(hw);
+
+ ret_val = atl2_phy_init(hw);
+
+ return ret_val;
+}
+
+/*
+ * Detects the current speed and duplex settings of the hardware.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * speed - Speed of the connection
+ * duplex - Duplex setting of the connection
+ */
+static s32 atl2_get_speed_and_duplex(struct atl2_hw *hw, u16 *speed,
+ u16 *duplex)
+{
+ s32 ret_val;
+ u16 phy_data;
+
+ /* Read PHY Specific Status Register (17) */
+ ret_val = atl2_read_phy_reg(hw, MII_ATLX_PSSR, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ if (!(phy_data & MII_ATLX_PSSR_SPD_DPLX_RESOLVED))
+ return ATLX_ERR_PHY_RES;
+
+ switch (phy_data & MII_ATLX_PSSR_SPEED) {
+ case MII_ATLX_PSSR_100MBS:
+ *speed = SPEED_100;
+ break;
+ case MII_ATLX_PSSR_10MBS:
+ *speed = SPEED_10;
+ break;
+ default:
+ return ATLX_ERR_PHY_SPEED;
+ break;
+ }
+
+ if (phy_data & MII_ATLX_PSSR_DPLX)
+ *duplex = FULL_DUPLEX;
+ else
+ *duplex = HALF_DUPLEX;
+
+ return 0;
+}
+
+/*
+ * Reads the value from a PHY register
+ * hw - Struct containing variables accessed by shared code
+ * reg_addr - address of the PHY register to read
+ */
+static s32 atl2_read_phy_reg(struct atl2_hw *hw, u16 reg_addr, u16 *phy_data)
+{
+ u32 val;
+ int i;
+
+ val = ((u32)(reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT |
+ MDIO_START |
+ MDIO_SUP_PREAMBLE |
+ MDIO_RW |
+ MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT;
+ ATL2_WRITE_REG(hw, REG_MDIO_CTRL, val);
+
+ wmb();
+
+ for (i = 0; i < MDIO_WAIT_TIMES; i++) {
+ udelay(2);
+ val = ATL2_READ_REG(hw, REG_MDIO_CTRL);
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ break;
+ wmb();
+ }
+ if (!(val & (MDIO_START | MDIO_BUSY))) {
+ *phy_data = (u16)val;
+ return 0;
+ }
+
+ return ATLX_ERR_PHY;
+}
+
+/*
+ * Writes a value to a PHY register
+ * hw - Struct containing variables accessed by shared code
+ * reg_addr - address of the PHY register to write
+ * data - data to write to the PHY
+ */
+static s32 atl2_write_phy_reg(struct atl2_hw *hw, u32 reg_addr, u16 phy_data)
+{
+ int i;
+ u32 val;
+
+ val = ((u32)(phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT |
+ (reg_addr & MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT |
+ MDIO_SUP_PREAMBLE |
+ MDIO_START |
+ MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT;
+ ATL2_WRITE_REG(hw, REG_MDIO_CTRL, val);
+
+ wmb();
+
+ for (i = 0; i < MDIO_WAIT_TIMES; i++) {
+ udelay(2);
+ val = ATL2_READ_REG(hw, REG_MDIO_CTRL);
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ break;
+
+ wmb();
+ }
+
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ return 0;
+
+ return ATLX_ERR_PHY;
+}
+
+/*
+ * Configures PHY autoneg and flow control advertisement settings
+ *
+ * hw - Struct containing variables accessed by shared code
+ */
+static s32 atl2_phy_setup_autoneg_adv(struct atl2_hw *hw)
+{
+ s32 ret_val;
+ s16 mii_autoneg_adv_reg;
+
+ /* Read the MII Auto-Neg Advertisement Register (Address 4). */
+ mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK;
+
+ /* Need to parse autoneg_advertised and set up
+ * the appropriate PHY registers. First we will parse for
+ * autoneg_advertised software override. Since we can advertise
+ * a plethora of combinations, we need to check each bit
+ * individually.
+ */
+
+ /* First we clear all the 10/100 mb speed bits in the Auto-Neg
+ * Advertisement Register (Address 4) and the 1000 mb speed bits in
+ * the 1000Base-T Control Register (Address 9). */
+ mii_autoneg_adv_reg &= ~MII_AR_SPEED_MASK;
+
+ /* Need to parse MediaType and setup the
+ * appropriate PHY registers. */
+ switch (hw->MediaType) {
+ case MEDIA_TYPE_AUTO_SENSOR:
+ mii_autoneg_adv_reg |=
+ (MII_AR_10T_HD_CAPS |
+ MII_AR_10T_FD_CAPS |
+ MII_AR_100TX_HD_CAPS|
+ MII_AR_100TX_FD_CAPS);
+ hw->autoneg_advertised =
+ ADVERTISE_10_HALF |
+ ADVERTISE_10_FULL |
+ ADVERTISE_100_HALF|
+ ADVERTISE_100_FULL;
+ break;
+ case MEDIA_TYPE_100M_FULL:
+ mii_autoneg_adv_reg |= MII_AR_100TX_FD_CAPS;
+ hw->autoneg_advertised = ADVERTISE_100_FULL;
+ break;
+ case MEDIA_TYPE_100M_HALF:
+ mii_autoneg_adv_reg |= MII_AR_100TX_HD_CAPS;
+ hw->autoneg_advertised = ADVERTISE_100_HALF;
+ break;
+ case MEDIA_TYPE_10M_FULL:
+ mii_autoneg_adv_reg |= MII_AR_10T_FD_CAPS;
+ hw->autoneg_advertised = ADVERTISE_10_FULL;
+ break;
+ default:
+ mii_autoneg_adv_reg |= MII_AR_10T_HD_CAPS;
+ hw->autoneg_advertised = ADVERTISE_10_HALF;
+ break;
+ }
+
+ /* flow control fixed to enable all */
+ mii_autoneg_adv_reg |= (MII_AR_ASM_DIR | MII_AR_PAUSE);
+
+ hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg;
+
+ ret_val = atl2_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg);
+
+ if (ret_val)
+ return ret_val;
+
+ return 0;
+}
+
+/*
+ * Resets the PHY and make all config validate
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Sets bit 15 and 12 of the MII Control regiser (for F001 bug)
+ */
+static s32 atl2_phy_commit(struct atl2_hw *hw)
+{
+ s32 ret_val;
+ u16 phy_data;
+
+ phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG;
+ ret_val = atl2_write_phy_reg(hw, MII_BMCR, phy_data);
+ if (ret_val) {
+ u32 val;
+ int i;
+ /* pcie serdes link may be down ! */
+ for (i = 0; i < 25; i++) {
+ msleep(1);
+ val = ATL2_READ_REG(hw, REG_MDIO_CTRL);
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ break;
+ }
+
+ if (0 != (val & (MDIO_START | MDIO_BUSY))) {
+ printk(KERN_ERR "atl2: PCIe link down for at least 25ms !\n");
+ return ret_val;
+ }
+ }
+ return 0;
+}
+
+static s32 atl2_phy_init(struct atl2_hw *hw)
+{
+ s32 ret_val;
+ u16 phy_val;
+
+ if (hw->phy_configured)
+ return 0;
+
+ /* Enable PHY */
+ ATL2_WRITE_REGW(hw, REG_PHY_ENABLE, 1);
+ ATL2_WRITE_FLUSH(hw);
+ msleep(1);
+
+ /* check if the PHY is in powersaving mode */
+ atl2_write_phy_reg(hw, MII_DBG_ADDR, 0);
+ atl2_read_phy_reg(hw, MII_DBG_DATA, &phy_val);
+
+ /* 024E / 124E 0r 0274 / 1274 ? */
+ if (phy_val & 0x1000) {
+ phy_val &= ~0x1000;
+ atl2_write_phy_reg(hw, MII_DBG_DATA, phy_val);
+ }
+
+ msleep(1);
+
+ /*Enable PHY LinkChange Interrupt */
+ ret_val = atl2_write_phy_reg(hw, 18, 0xC00);
+ if (ret_val)
+ return ret_val;
+
+ /* setup AutoNeg parameters */
+ ret_val = atl2_phy_setup_autoneg_adv(hw);
+ if (ret_val)
+ return ret_val;
+
+ /* SW.Reset & En-Auto-Neg to restart Auto-Neg */
+ ret_val = atl2_phy_commit(hw);
+ if (ret_val)
+ return ret_val;
+
+ hw->phy_configured = true;
+
+ return ret_val;
+}
+
+static void atl2_set_mac_addr(struct atl2_hw *hw)
+{
+ u32 value;
+ /* 00-0B-6A-F6-00-DC
+ * 0: 6AF600DC 1: 000B
+ * low dword */
+ value = (((u32)hw->mac_addr[2]) << 24) |
+ (((u32)hw->mac_addr[3]) << 16) |
+ (((u32)hw->mac_addr[4]) << 8) |
+ (((u32)hw->mac_addr[5]));
+ ATL2_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value);
+ /* hight dword */
+ value = (((u32)hw->mac_addr[0]) << 8) |
+ (((u32)hw->mac_addr[1]));
+ ATL2_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value);
+}
+
+/*
+ * check_eeprom_exist
+ * return 0 if eeprom exist
+ */
+static int atl2_check_eeprom_exist(struct atl2_hw *hw)
+{
+ u32 value;
+
+ value = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL);
+ if (value & SPI_FLASH_CTRL_EN_VPD) {
+ value &= ~SPI_FLASH_CTRL_EN_VPD;
+ ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value);
+ }
+ value = ATL2_READ_REGW(hw, REG_PCIE_CAP_LIST);
+ return ((value & 0xFF00) == 0x6C00) ? 0 : 1;
+}
+
+/* FIXME: This doesn't look right. -- CHS */
+static bool atl2_write_eeprom(struct atl2_hw *hw, u32 offset, u32 value)
+{
+ return true;
+}
+
+static bool atl2_read_eeprom(struct atl2_hw *hw, u32 Offset, u32 *pValue)
+{
+ int i;
+ u32 Control;
+
+ if (Offset & 0x3)
+ return false; /* address do not align */
+
+ ATL2_WRITE_REG(hw, REG_VPD_DATA, 0);
+ Control = (Offset & VPD_CAP_VPD_ADDR_MASK) << VPD_CAP_VPD_ADDR_SHIFT;
+ ATL2_WRITE_REG(hw, REG_VPD_CAP, Control);
+
+ for (i = 0; i < 10; i++) {
+ msleep(2);
+ Control = ATL2_READ_REG(hw, REG_VPD_CAP);
+ if (Control & VPD_CAP_VPD_FLAG)
+ break;
+ }
+
+ if (Control & VPD_CAP_VPD_FLAG) {
+ *pValue = ATL2_READ_REG(hw, REG_VPD_DATA);
+ return true;
+ }
+ return false; /* timeout */
+}
+
+static void atl2_force_ps(struct atl2_hw *hw)
+{
+ u16 phy_val;
+
+ atl2_write_phy_reg(hw, MII_DBG_ADDR, 0);
+ atl2_read_phy_reg(hw, MII_DBG_DATA, &phy_val);
+ atl2_write_phy_reg(hw, MII_DBG_DATA, phy_val | 0x1000);
+
+ atl2_write_phy_reg(hw, MII_DBG_ADDR, 2);
+ atl2_write_phy_reg(hw, MII_DBG_DATA, 0x3000);
+ atl2_write_phy_reg(hw, MII_DBG_ADDR, 3);
+ atl2_write_phy_reg(hw, MII_DBG_DATA, 0);
+}
+
+/* This is the only thing that needs to be changed to adjust the
+ * maximum number of ports that the driver can manage.
+ */
+#define ATL2_MAX_NIC 4
+
+#define OPTION_UNSET -1
+#define OPTION_DISABLED 0
+#define OPTION_ENABLED 1
+
+/* All parameters are treated the same, as an integer array of values.
+ * This macro just reduces the need to repeat the same declaration code
+ * over and over (plus this helps to avoid typo bugs).
+ */
+#define ATL2_PARAM_INIT {[0 ... ATL2_MAX_NIC] = OPTION_UNSET}
+#ifndef module_param_array
+/* Module Parameters are always initialized to -1, so that the driver
+ * can tell the difference between no user specified value or the
+ * user asking for the default value.
+ * The true default values are loaded in when atl2_check_options is called.
+ *
+ * This is a GCC extension to ANSI C.
+ * See the item "Labeled Elements in Initializers" in the section
+ * "Extensions to the C Language Family" of the GCC documentation.
+ */
+
+#define ATL2_PARAM(X, desc) \
+ static const int __devinitdata X[ATL2_MAX_NIC + 1] = ATL2_PARAM_INIT; \
+ MODULE_PARM(X, "1-" __MODULE_STRING(ATL2_MAX_NIC) "i"); \
+ MODULE_PARM_DESC(X, desc);
+#else
+#define ATL2_PARAM(X, desc) \
+ static int __devinitdata X[ATL2_MAX_NIC+1] = ATL2_PARAM_INIT; \
+ static unsigned int num_##X; \
+ module_param_array_named(X, X, int, &num_##X, 0); \
+ MODULE_PARM_DESC(X, desc);
+#endif
+
+/*
+ * Transmit Memory Size
+ * Valid Range: 64-2048
+ * Default Value: 128
+ */
+#define ATL2_MIN_TX_MEMSIZE 4 /* 4KB */
+#define ATL2_MAX_TX_MEMSIZE 64 /* 64KB */
+#define ATL2_DEFAULT_TX_MEMSIZE 8 /* 8KB */
+ATL2_PARAM(TxMemSize, "Bytes of Transmit Memory");
+
+/*
+ * Receive Memory Block Count
+ * Valid Range: 16-512
+ * Default Value: 128
+ */
+#define ATL2_MIN_RXD_COUNT 16
+#define ATL2_MAX_RXD_COUNT 512
+#define ATL2_DEFAULT_RXD_COUNT 64
+ATL2_PARAM(RxMemBlock, "Number of receive memory block");
+
+/*
+ * User Specified MediaType Override
+ *
+ * Valid Range: 0-5
+ * - 0 - auto-negotiate at all supported speeds
+ * - 1 - only link at 1000Mbps Full Duplex
+ * - 2 - only link at 100Mbps Full Duplex
+ * - 3 - only link at 100Mbps Half Duplex
+ * - 4 - only link at 10Mbps Full Duplex
+ * - 5 - only link at 10Mbps Half Duplex
+ * Default Value: 0
+ */
+ATL2_PARAM(MediaType, "MediaType Select");
+
+/*
+ * Interrupt Moderate Timer in units of 2048 ns (~2 us)
+ * Valid Range: 10-65535
+ * Default Value: 45000(90ms)
+ */
+#define INT_MOD_DEFAULT_CNT 100 /* 200us */
+#define INT_MOD_MAX_CNT 65000
+#define INT_MOD_MIN_CNT 50
+ATL2_PARAM(IntModTimer, "Interrupt Moderator Timer");
+
+/*
+ * FlashVendor
+ * Valid Range: 0-2
+ * 0 - Atmel
+ * 1 - SST
+ * 2 - ST
+ */
+ATL2_PARAM(FlashVendor, "SPI Flash Vendor");
+
+#define AUTONEG_ADV_DEFAULT 0x2F
+#define AUTONEG_ADV_MASK 0x2F
+#define FLOW_CONTROL_DEFAULT FLOW_CONTROL_FULL
+
+#define FLASH_VENDOR_DEFAULT 0
+#define FLASH_VENDOR_MIN 0
+#define FLASH_VENDOR_MAX 2
+
+struct atl2_option {
+ enum { enable_option, range_option, list_option } type;
+ char *name;
+ char *err;
+ int def;
+ union {
+ struct { /* range_option info */
+ int min;
+ int max;
+ } r;
+ struct { /* list_option info */
+ int nr;
+ struct atl2_opt_list { int i; char *str; } *p;
+ } l;
+ } arg;
+};
+
+static int __devinit atl2_validate_option(int *value, struct atl2_option *opt)
+{
+ int i;
+ struct atl2_opt_list *ent;
+
+ if (*value == OPTION_UNSET) {
+ *value = opt->def;
+ return 0;
+ }
+
+ switch (opt->type) {
+ case enable_option:
+ switch (*value) {
+ case OPTION_ENABLED:
+ printk(KERN_INFO "%s Enabled\n", opt->name);
+ return 0;
+ break;
+ case OPTION_DISABLED:
+ printk(KERN_INFO "%s Disabled\n", opt->name);
+ return 0;
+ break;
+ }
+ break;
+ case range_option:
+ if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) {
+ printk(KERN_INFO "%s set to %i\n", opt->name, *value);
+ return 0;
+ }
+ break;
+ case list_option:
+ for (i = 0; i < opt->arg.l.nr; i++) {
+ ent = &opt->arg.l.p[i];
+ if (*value == ent->i) {
+ if (ent->str[0] != '\0')
+ printk(KERN_INFO "%s\n", ent->str);
+ return 0;
+ }
+ }
+ break;
+ default:
+ BUG();
+ }
+
+ printk(KERN_INFO "Invalid %s specified (%i) %s\n",
+ opt->name, *value, opt->err);
+ *value = opt->def;
+ return -1;
+}
+
+/*
+ * atl2_check_options - Range Checking for Command Line Parameters
+ * @adapter: board private structure
+ *
+ * This routine checks all command line parameters for valid user
+ * input. If an invalid value is given, or if no user specified
+ * value exists, a default value is used. The final value is stored
+ * in a variable in the adapter structure.
+ */
+static void __devinit atl2_check_options(struct atl2_adapter *adapter)
+{
+ int val;
+ struct atl2_option opt;
+ int bd = adapter->bd_number;
+ if (bd >= ATL2_MAX_NIC) {
+ printk(KERN_NOTICE "Warning: no configuration for board #%i\n",
+ bd);
+ printk(KERN_NOTICE "Using defaults for all values\n");
+#ifndef module_param_array
+ bd = ATL2_MAX_NIC;
+#endif
+ }
+
+ /* Bytes of Transmit Memory */
+ opt.type = range_option;
+ opt.name = "Bytes of Transmit Memory";
+ opt.err = "using default of " __MODULE_STRING(ATL2_DEFAULT_TX_MEMSIZE);
+ opt.def = ATL2_DEFAULT_TX_MEMSIZE;
+ opt.arg.r.min = ATL2_MIN_TX_MEMSIZE;
+ opt.arg.r.max = ATL2_MAX_TX_MEMSIZE;
+#ifdef module_param_array
+ if (num_TxMemSize > bd) {
+#endif
+ val = TxMemSize[bd];
+ atl2_validate_option(&val, &opt);
+ adapter->txd_ring_size = ((u32) val) * 1024;
+#ifdef module_param_array
+ } else
+ adapter->txd_ring_size = ((u32)opt.def) * 1024;
+#endif
+ /* txs ring size: */
+ adapter->txs_ring_size = adapter->txd_ring_size / 128;
+ if (adapter->txs_ring_size > 160)
+ adapter->txs_ring_size = 160;
+
+ /* Receive Memory Block Count */
+ opt.type = range_option;
+ opt.name = "Number of receive memory block";
+ opt.err = "using default of " __MODULE_STRING(ATL2_DEFAULT_RXD_COUNT);
+ opt.def = ATL2_DEFAULT_RXD_COUNT;
+ opt.arg.r.min = ATL2_MIN_RXD_COUNT;
+ opt.arg.r.max = ATL2_MAX_RXD_COUNT;
+#ifdef module_param_array
+ if (num_RxMemBlock > bd) {
+#endif
+ val = RxMemBlock[bd];
+ atl2_validate_option(&val, &opt);
+ adapter->rxd_ring_size = (u32)val;
+ /* FIXME */
+ /* ((u16)val)&~1; */ /* even number */
+#ifdef module_param_array
+ } else
+ adapter->rxd_ring_size = (u32)opt.def;
+#endif
+ /* init RXD Flow control value */
+ adapter->hw.fc_rxd_hi = (adapter->rxd_ring_size / 8) * 7;
+ adapter->hw.fc_rxd_lo = (ATL2_MIN_RXD_COUNT / 8) >
+ (adapter->rxd_ring_size / 12) ? (ATL2_MIN_RXD_COUNT / 8) :
+ (adapter->rxd_ring_size / 12);
+
+ /* Interrupt Moderate Timer */
+ opt.type = range_option;
+ opt.name = "Interrupt Moderate Timer";
+ opt.err = "using default of " __MODULE_STRING(INT_MOD_DEFAULT_CNT);
+ opt.def = INT_MOD_DEFAULT_CNT;
+ opt.arg.r.min = INT_MOD_MIN_CNT;
+ opt.arg.r.max = INT_MOD_MAX_CNT;
+#ifdef module_param_array
+ if (num_IntModTimer > bd) {
+#endif
+ val = IntModTimer[bd];
+ atl2_validate_option(&val, &opt);
+ adapter->imt = (u16) val;
+#ifdef module_param_array
+ } else
+ adapter->imt = (u16)(opt.def);
+#endif
+ /* Flash Vendor */
+ opt.type = range_option;
+ opt.name = "SPI Flash Vendor";
+ opt.err = "using default of " __MODULE_STRING(FLASH_VENDOR_DEFAULT);
+ opt.def = FLASH_VENDOR_DEFAULT;
+ opt.arg.r.min = FLASH_VENDOR_MIN;
+ opt.arg.r.max = FLASH_VENDOR_MAX;
+#ifdef module_param_array
+ if (num_FlashVendor > bd) {
+#endif
+ val = FlashVendor[bd];
+ atl2_validate_option(&val, &opt);
+ adapter->hw.flash_vendor = (u8) val;
+#ifdef module_param_array
+ } else
+ adapter->hw.flash_vendor = (u8)(opt.def);
+#endif
+ /* MediaType */
+ opt.type = range_option;
+ opt.name = "Speed/Duplex Selection";
+ opt.err = "using default of " __MODULE_STRING(MEDIA_TYPE_AUTO_SENSOR);
+ opt.def = MEDIA_TYPE_AUTO_SENSOR;
+ opt.arg.r.min = MEDIA_TYPE_AUTO_SENSOR;
+ opt.arg.r.max = MEDIA_TYPE_10M_HALF;
+#ifdef module_param_array
+ if (num_MediaType > bd) {
+#endif
+ val = MediaType[bd];
+ atl2_validate_option(&val, &opt);
+ adapter->hw.MediaType = (u16) val;
+#ifdef module_param_array
+ } else
+ adapter->hw.MediaType = (u16)(opt.def);
+#endif
+}
diff --git a/drivers/net/ethernet/atheros/atlx/atl2.h b/drivers/net/ethernet/atheros/atlx/atl2.h
new file mode 100644
index 0000000..bf9016e
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atlx/atl2.h
@@ -0,0 +1,525 @@
+/* atl2.h -- atl2 driver definitions
+ *
+ * Copyright(c) 2007 Atheros Corporation. All rights reserved.
+ * Copyright(c) 2006 xiong huang <xiong.huang@atheros.com>
+ * Copyright(c) 2007 Chris Snook <csnook@redhat.com>
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ */
+
+#ifndef _ATL2_H_
+#define _ATL2_H_
+
+#include <linux/atomic.h>
+#include <linux/netdevice.h>
+
+#ifndef _ATL2_HW_H_
+#define _ATL2_HW_H_
+
+#ifndef _ATL2_OSDEP_H_
+#define _ATL2_OSDEP_H_
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+
+#include "atlx.h"
+
+#ifdef ETHTOOL_OPS_COMPAT
+extern int ethtool_ioctl(struct ifreq *ifr);
+#endif
+
+#define PCI_COMMAND_REGISTER PCI_COMMAND
+#define CMD_MEM_WRT_INVALIDATE PCI_COMMAND_INVALIDATE
+#define ETH_ADDR_LEN ETH_ALEN
+
+#define ATL2_WRITE_REG(a, reg, value) (iowrite32((value), \
+ ((a)->hw_addr + (reg))))
+
+#define ATL2_WRITE_FLUSH(a) (ioread32((a)->hw_addr))
+
+#define ATL2_READ_REG(a, reg) (ioread32((a)->hw_addr + (reg)))
+
+#define ATL2_WRITE_REGB(a, reg, value) (iowrite8((value), \
+ ((a)->hw_addr + (reg))))
+
+#define ATL2_READ_REGB(a, reg) (ioread8((a)->hw_addr + (reg)))
+
+#define ATL2_WRITE_REGW(a, reg, value) (iowrite16((value), \
+ ((a)->hw_addr + (reg))))
+
+#define ATL2_READ_REGW(a, reg) (ioread16((a)->hw_addr + (reg)))
+
+#define ATL2_WRITE_REG_ARRAY(a, reg, offset, value) \
+ (iowrite32((value), (((a)->hw_addr + (reg)) + ((offset) << 2))))
+
+#define ATL2_READ_REG_ARRAY(a, reg, offset) \
+ (ioread32(((a)->hw_addr + (reg)) + ((offset) << 2)))
+
+#endif /* _ATL2_OSDEP_H_ */
+
+struct atl2_adapter;
+struct atl2_hw;
+
+/* function prototype */
+static s32 atl2_reset_hw(struct atl2_hw *hw);
+static s32 atl2_read_mac_addr(struct atl2_hw *hw);
+static s32 atl2_init_hw(struct atl2_hw *hw);
+static s32 atl2_get_speed_and_duplex(struct atl2_hw *hw, u16 *speed,
+ u16 *duplex);
+static u32 atl2_hash_mc_addr(struct atl2_hw *hw, u8 *mc_addr);
+static void atl2_hash_set(struct atl2_hw *hw, u32 hash_value);
+static s32 atl2_read_phy_reg(struct atl2_hw *hw, u16 reg_addr, u16 *phy_data);
+static s32 atl2_write_phy_reg(struct atl2_hw *hw, u32 reg_addr, u16 phy_data);
+static void atl2_read_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value);
+static void atl2_write_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value);
+static void atl2_set_mac_addr(struct atl2_hw *hw);
+static bool atl2_read_eeprom(struct atl2_hw *hw, u32 Offset, u32 *pValue);
+static bool atl2_write_eeprom(struct atl2_hw *hw, u32 offset, u32 value);
+static s32 atl2_phy_init(struct atl2_hw *hw);
+static int atl2_check_eeprom_exist(struct atl2_hw *hw);
+static void atl2_force_ps(struct atl2_hw *hw);
+
+/* register definition */
+
+/* Block IDLE Status Register */
+#define IDLE_STATUS_RXMAC 1 /* 1: RXMAC is non-IDLE */
+#define IDLE_STATUS_TXMAC 2 /* 1: TXMAC is non-IDLE */
+#define IDLE_STATUS_DMAR 8 /* 1: DMAR is non-IDLE */
+#define IDLE_STATUS_DMAW 4 /* 1: DMAW is non-IDLE */
+
+/* MDIO Control Register */
+#define MDIO_WAIT_TIMES 10
+
+/* MAC Control Register */
+#define MAC_CTRL_DBG_TX_BKPRESURE 0x100000 /* 1: TX max backoff */
+#define MAC_CTRL_MACLP_CLK_PHY 0x8000000 /* 1: 25MHz from phy */
+#define MAC_CTRL_HALF_LEFT_BUF_SHIFT 28
+#define MAC_CTRL_HALF_LEFT_BUF_MASK 0xF /* MAC retry buf x32B */
+
+/* Internal SRAM Partition Register */
+#define REG_SRAM_TXRAM_END 0x1500 /* Internal tail address of TXRAM
+ * default: 2byte*1024 */
+#define REG_SRAM_RXRAM_END 0x1502 /* Internal tail address of RXRAM
+ * default: 2byte*1024 */
+
+/* Descriptor Control register */
+#define REG_TXD_BASE_ADDR_LO 0x1544 /* The base address of the Transmit
+ * Data Mem low 32-bit(dword align) */
+#define REG_TXD_MEM_SIZE 0x1548 /* Transmit Data Memory size(by
+ * double word , max 256KB) */
+#define REG_TXS_BASE_ADDR_LO 0x154C /* The base address of the Transmit
+ * Status Memory low 32-bit(dword word
+ * align) */
+#define REG_TXS_MEM_SIZE 0x1550 /* double word unit, max 4*2047
+ * bytes. */
+#define REG_RXD_BASE_ADDR_LO 0x1554 /* The base address of the Transmit
+ * Status Memory low 32-bit(unit 8
+ * bytes) */
+#define REG_RXD_BUF_NUM 0x1558 /* Receive Data & Status Memory buffer
+ * number (unit 1536bytes, max
+ * 1536*2047) */
+
+/* DMAR Control Register */
+#define REG_DMAR 0x1580
+#define DMAR_EN 0x1 /* 1: Enable DMAR */
+
+/* TX Cur-Through (early tx threshold) Control Register */
+#define REG_TX_CUT_THRESH 0x1590 /* TxMac begin transmit packet
+ * threshold(unit word) */
+
+/* DMAW Control Register */
+#define REG_DMAW 0x15A0
+#define DMAW_EN 0x1
+
+/* Flow control register */
+#define REG_PAUSE_ON_TH 0x15A8 /* RXD high watermark of overflow
+ * threshold configuration register */
+#define REG_PAUSE_OFF_TH 0x15AA /* RXD lower watermark of overflow
+ * threshold configuration register */
+
+/* Mailbox Register */
+#define REG_MB_TXD_WR_IDX 0x15f0 /* double word align */
+#define REG_MB_RXD_RD_IDX 0x15F4 /* RXD Read index (unit: 1536byets) */
+
+/* Interrupt Status Register */
+#define ISR_TIMER 1 /* Interrupt when Timer counts down to zero */
+#define ISR_MANUAL 2 /* Software manual interrupt, for debug. Set
+ * when SW_MAN_INT_EN is set in Table 51
+ * Selene Master Control Register
+ * (Offset 0x1400). */
+#define ISR_RXF_OV 4 /* RXF overflow interrupt */
+#define ISR_TXF_UR 8 /* TXF underrun interrupt */
+#define ISR_TXS_OV 0x10 /* Internal transmit status buffer full
+ * interrupt */
+#define ISR_RXS_OV 0x20 /* Internal receive status buffer full
+ * interrupt */
+#define ISR_LINK_CHG 0x40 /* Link Status Change Interrupt */
+#define ISR_HOST_TXD_UR 0x80
+#define ISR_HOST_RXD_OV 0x100 /* Host rx data memory full , one pulse */
+#define ISR_DMAR_TO_RST 0x200 /* DMAR op timeout interrupt. SW should
+ * do Reset */
+#define ISR_DMAW_TO_RST 0x400
+#define ISR_PHY 0x800 /* phy interrupt */
+#define ISR_TS_UPDATE 0x10000 /* interrupt after new tx pkt status written
+ * to host */
+#define ISR_RS_UPDATE 0x20000 /* interrupt ater new rx pkt status written
+ * to host. */
+#define ISR_TX_EARLY 0x40000 /* interrupt when txmac begin transmit one
+ * packet */
+
+#define ISR_TX_EVENT (ISR_TXF_UR | ISR_TXS_OV | ISR_HOST_TXD_UR |\
+ ISR_TS_UPDATE | ISR_TX_EARLY)
+#define ISR_RX_EVENT (ISR_RXF_OV | ISR_RXS_OV | ISR_HOST_RXD_OV |\
+ ISR_RS_UPDATE)
+
+#define IMR_NORMAL_MASK (\
+ /*ISR_LINK_CHG |*/\
+ ISR_MANUAL |\
+ ISR_DMAR_TO_RST |\
+ ISR_DMAW_TO_RST |\
+ ISR_PHY |\
+ ISR_PHY_LINKDOWN |\
+ ISR_TS_UPDATE |\
+ ISR_RS_UPDATE)
+
+/* Receive MAC Statistics Registers */
+#define REG_STS_RX_PAUSE 0x1700 /* Num pause packets received */
+#define REG_STS_RXD_OV 0x1704 /* Num frames dropped due to RX
+ * FIFO overflow */
+#define REG_STS_RXS_OV 0x1708 /* Num frames dropped due to RX
+ * Status Buffer Overflow */
+#define REG_STS_RX_FILTER 0x170C /* Num packets dropped due to
+ * address filtering */
+
+/* MII definitions */
+
+/* PHY Common Register */
+#define MII_SMARTSPEED 0x14
+#define MII_DBG_ADDR 0x1D
+#define MII_DBG_DATA 0x1E
+
+/* PCI Command Register Bit Definitions */
+#define PCI_REG_COMMAND 0x04
+#define CMD_IO_SPACE 0x0001
+#define CMD_MEMORY_SPACE 0x0002
+#define CMD_BUS_MASTER 0x0004
+
+#define MEDIA_TYPE_100M_FULL 1
+#define MEDIA_TYPE_100M_HALF 2
+#define MEDIA_TYPE_10M_FULL 3
+#define MEDIA_TYPE_10M_HALF 4
+
+#define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x000F /* Everything */
+
+/* The size (in bytes) of a ethernet packet */
+#define ENET_HEADER_SIZE 14
+#define MAXIMUM_ETHERNET_FRAME_SIZE 1518 /* with FCS */
+#define MINIMUM_ETHERNET_FRAME_SIZE 64 /* with FCS */
+#define ETHERNET_FCS_SIZE 4
+#define MAX_JUMBO_FRAME_SIZE 0x2000
+#define VLAN_SIZE 4
+
+struct tx_pkt_header {
+ unsigned pkt_size:11;
+ unsigned:4; /* reserved */
+ unsigned ins_vlan:1; /* txmac should insert vlan */
+ unsigned short vlan; /* vlan tag */
+};
+/* FIXME: replace above bitfields with MASK/SHIFT defines below */
+#define TX_PKT_HEADER_SIZE_MASK 0x7FF
+#define TX_PKT_HEADER_SIZE_SHIFT 0
+#define TX_PKT_HEADER_INS_VLAN_MASK 0x1
+#define TX_PKT_HEADER_INS_VLAN_SHIFT 15
+#define TX_PKT_HEADER_VLAN_TAG_MASK 0xFFFF
+#define TX_PKT_HEADER_VLAN_TAG_SHIFT 16
+
+struct tx_pkt_status {
+ unsigned pkt_size:11;
+ unsigned:5; /* reserved */
+ unsigned ok:1; /* current packet transmitted without error */
+ unsigned bcast:1; /* broadcast packet */
+ unsigned mcast:1; /* multicast packet */
+ unsigned pause:1; /* transmiited a pause frame */
+ unsigned ctrl:1;
+ unsigned defer:1; /* current packet is xmitted with defer */
+ unsigned exc_defer:1;
+ unsigned single_col:1;
+ unsigned multi_col:1;
+ unsigned late_col:1;
+ unsigned abort_col:1;
+ unsigned underun:1; /* current packet is aborted
+ * due to txram underrun */
+ unsigned:3; /* reserved */
+ unsigned update:1; /* always 1'b1 in tx_status_buf */
+};
+/* FIXME: replace above bitfields with MASK/SHIFT defines below */
+#define TX_PKT_STATUS_SIZE_MASK 0x7FF
+#define TX_PKT_STATUS_SIZE_SHIFT 0
+#define TX_PKT_STATUS_OK_MASK 0x1
+#define TX_PKT_STATUS_OK_SHIFT 16
+#define TX_PKT_STATUS_BCAST_MASK 0x1
+#define TX_PKT_STATUS_BCAST_SHIFT 17
+#define TX_PKT_STATUS_MCAST_MASK 0x1
+#define TX_PKT_STATUS_MCAST_SHIFT 18
+#define TX_PKT_STATUS_PAUSE_MASK 0x1
+#define TX_PKT_STATUS_PAUSE_SHIFT 19
+#define TX_PKT_STATUS_CTRL_MASK 0x1
+#define TX_PKT_STATUS_CTRL_SHIFT 20
+#define TX_PKT_STATUS_DEFER_MASK 0x1
+#define TX_PKT_STATUS_DEFER_SHIFT 21
+#define TX_PKT_STATUS_EXC_DEFER_MASK 0x1
+#define TX_PKT_STATUS_EXC_DEFER_SHIFT 22
+#define TX_PKT_STATUS_SINGLE_COL_MASK 0x1
+#define TX_PKT_STATUS_SINGLE_COL_SHIFT 23
+#define TX_PKT_STATUS_MULTI_COL_MASK 0x1
+#define TX_PKT_STATUS_MULTI_COL_SHIFT 24
+#define TX_PKT_STATUS_LATE_COL_MASK 0x1
+#define TX_PKT_STATUS_LATE_COL_SHIFT 25
+#define TX_PKT_STATUS_ABORT_COL_MASK 0x1
+#define TX_PKT_STATUS_ABORT_COL_SHIFT 26
+#define TX_PKT_STATUS_UNDERRUN_MASK 0x1
+#define TX_PKT_STATUS_UNDERRUN_SHIFT 27
+#define TX_PKT_STATUS_UPDATE_MASK 0x1
+#define TX_PKT_STATUS_UPDATE_SHIFT 31
+
+struct rx_pkt_status {
+ unsigned pkt_size:11; /* packet size, max 2047 bytes */
+ unsigned:5; /* reserved */
+ unsigned ok:1; /* current packet received ok without error */
+ unsigned bcast:1; /* current packet is broadcast */
+ unsigned mcast:1; /* current packet is multicast */
+ unsigned pause:1;
+ unsigned ctrl:1;
+ unsigned crc:1; /* received a packet with crc error */
+ unsigned code:1; /* received a packet with code error */
+ unsigned runt:1; /* received a packet less than 64 bytes
+ * with good crc */
+ unsigned frag:1; /* received a packet less than 64 bytes
+ * with bad crc */
+ unsigned trunc:1; /* current frame truncated due to rxram full */
+ unsigned align:1; /* this packet is alignment error */
+ unsigned vlan:1; /* this packet has vlan */
+ unsigned:3; /* reserved */
+ unsigned update:1;
+ unsigned short vtag; /* vlan tag */
+ unsigned:16;
+};
+/* FIXME: replace above bitfields with MASK/SHIFT defines below */
+#define RX_PKT_STATUS_SIZE_MASK 0x7FF
+#define RX_PKT_STATUS_SIZE_SHIFT 0
+#define RX_PKT_STATUS_OK_MASK 0x1
+#define RX_PKT_STATUS_OK_SHIFT 16
+#define RX_PKT_STATUS_BCAST_MASK 0x1
+#define RX_PKT_STATUS_BCAST_SHIFT 17
+#define RX_PKT_STATUS_MCAST_MASK 0x1
+#define RX_PKT_STATUS_MCAST_SHIFT 18
+#define RX_PKT_STATUS_PAUSE_MASK 0x1
+#define RX_PKT_STATUS_PAUSE_SHIFT 19
+#define RX_PKT_STATUS_CTRL_MASK 0x1
+#define RX_PKT_STATUS_CTRL_SHIFT 20
+#define RX_PKT_STATUS_CRC_MASK 0x1
+#define RX_PKT_STATUS_CRC_SHIFT 21
+#define RX_PKT_STATUS_CODE_MASK 0x1
+#define RX_PKT_STATUS_CODE_SHIFT 22
+#define RX_PKT_STATUS_RUNT_MASK 0x1
+#define RX_PKT_STATUS_RUNT_SHIFT 23
+#define RX_PKT_STATUS_FRAG_MASK 0x1
+#define RX_PKT_STATUS_FRAG_SHIFT 24
+#define RX_PKT_STATUS_TRUNK_MASK 0x1
+#define RX_PKT_STATUS_TRUNK_SHIFT 25
+#define RX_PKT_STATUS_ALIGN_MASK 0x1
+#define RX_PKT_STATUS_ALIGN_SHIFT 26
+#define RX_PKT_STATUS_VLAN_MASK 0x1
+#define RX_PKT_STATUS_VLAN_SHIFT 27
+#define RX_PKT_STATUS_UPDATE_MASK 0x1
+#define RX_PKT_STATUS_UPDATE_SHIFT 31
+#define RX_PKT_STATUS_VLAN_TAG_MASK 0xFFFF
+#define RX_PKT_STATUS_VLAN_TAG_SHIFT 32
+
+struct rx_desc {
+ struct rx_pkt_status status;
+ unsigned char packet[1536-sizeof(struct rx_pkt_status)];
+};
+
+enum atl2_speed_duplex {
+ atl2_10_half = 0,
+ atl2_10_full = 1,
+ atl2_100_half = 2,
+ atl2_100_full = 3
+};
+
+struct atl2_spi_flash_dev {
+ const char *manu_name; /* manufacturer id */
+ /* op-code */
+ u8 cmdWRSR;
+ u8 cmdREAD;
+ u8 cmdPROGRAM;
+ u8 cmdWREN;
+ u8 cmdWRDI;
+ u8 cmdRDSR;
+ u8 cmdRDID;
+ u8 cmdSECTOR_ERASE;
+ u8 cmdCHIP_ERASE;
+};
+
+/* Structure containing variables used by the shared code (atl2_hw.c) */
+struct atl2_hw {
+ u8 __iomem *hw_addr;
+ void *back;
+
+ u8 preamble_len;
+ u8 max_retry; /* Retransmission maximum, afterwards the
+ * packet will be discarded. */
+ u8 jam_ipg; /* IPG to start JAM for collision based flow
+ * control in half-duplex mode. In unit of
+ * 8-bit time. */
+ u8 ipgt; /* Desired back to back inter-packet gap. The
+ * default is 96-bit time. */
+ u8 min_ifg; /* Minimum number of IFG to enforce in between
+ * RX frames. Frame gap below such IFP is
+ * dropped. */
+ u8 ipgr1; /* 64bit Carrier-Sense window */
+ u8 ipgr2; /* 96-bit IPG window */
+ u8 retry_buf; /* When half-duplex mode, should hold some
+ * bytes for mac retry . (8*4bytes unit) */
+
+ u16 fc_rxd_hi;
+ u16 fc_rxd_lo;
+ u16 lcol; /* Collision Window */
+ u16 max_frame_size;
+
+ u16 MediaType;
+ u16 autoneg_advertised;
+ u16 pci_cmd_word;
+
+ u16 mii_autoneg_adv_reg;
+
+ u32 mem_rang;
+ u32 txcw;
+ u32 mc_filter_type;
+ u32 num_mc_addrs;
+ u32 collision_delta;
+ u32 tx_packet_delta;
+ u16 phy_spd_default;
+
+ u16 device_id;
+ u16 vendor_id;
+ u16 subsystem_id;
+ u16 subsystem_vendor_id;
+ u8 revision_id;
+
+ /* spi flash */
+ u8 flash_vendor;
+
+ u8 dma_fairness;
+ u8 mac_addr[NODE_ADDRESS_SIZE];
+ u8 perm_mac_addr[NODE_ADDRESS_SIZE];
+
+ /* FIXME */
+ /* bool phy_preamble_sup; */
+ bool phy_configured;
+};
+
+#endif /* _ATL2_HW_H_ */
+
+struct atl2_ring_header {
+ /* pointer to the descriptor ring memory */
+ void *desc;
+ /* physical address of the descriptor ring */
+ dma_addr_t dma;
+ /* length of descriptor ring in bytes */
+ unsigned int size;
+};
+
+/* board specific private data structure */
+struct atl2_adapter {
+ /* OS defined structs */
+ struct net_device *netdev;
+ struct pci_dev *pdev;
+ u32 wol;
+ u16 link_speed;
+ u16 link_duplex;
+
+ spinlock_t stats_lock;
+
+ struct work_struct reset_task;
+ struct work_struct link_chg_task;
+ struct timer_list watchdog_timer;
+ struct timer_list phy_config_timer;
+
+ unsigned long cfg_phy;
+ bool mac_disabled;
+
+ /* All Descriptor memory */
+ dma_addr_t ring_dma;
+ void *ring_vir_addr;
+ int ring_size;
+
+ struct tx_pkt_header *txd_ring;
+ dma_addr_t txd_dma;
+
+ struct tx_pkt_status *txs_ring;
+ dma_addr_t txs_dma;
+
+ struct rx_desc *rxd_ring;
+ dma_addr_t rxd_dma;
+
+ u32 txd_ring_size; /* bytes per unit */
+ u32 txs_ring_size; /* dwords per unit */
+ u32 rxd_ring_size; /* 1536 bytes per unit */
+
+ /* read /write ptr: */
+ /* host */
+ u32 txd_write_ptr;
+ u32 txs_next_clear;
+ u32 rxd_read_ptr;
+
+ /* nic */
+ atomic_t txd_read_ptr;
+ atomic_t txs_write_ptr;
+ u32 rxd_write_ptr;
+
+ /* Interrupt Moderator timer ( 2us resolution) */
+ u16 imt;
+ /* Interrupt Clear timer (2us resolution) */
+ u16 ict;
+
+ unsigned long flags;
+ /* structs defined in atl2_hw.h */
+ u32 bd_number; /* board number */
+ bool pci_using_64;
+ bool have_msi;
+ struct atl2_hw hw;
+
+ u32 usr_cmd;
+ /* FIXME */
+ /* u32 regs_buff[ATL2_REGS_LEN]; */
+ u32 pci_state[16];
+
+ u32 *config_space;
+};
+
+enum atl2_state_t {
+ __ATL2_TESTING,
+ __ATL2_RESETTING,
+ __ATL2_DOWN
+};
+
+#endif /* _ATL2_H_ */
diff --git a/drivers/net/ethernet/atheros/atlx/atlx.c b/drivers/net/ethernet/atheros/atlx/atlx.c
new file mode 100644
index 0000000..ee3da13
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atlx/atlx.c
@@ -0,0 +1,274 @@
+/* atlx.c -- common functions for Attansic network drivers
+ *
+ * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved.
+ * Copyright(c) 2006 - 2007 Chris Snook <csnook@redhat.com>
+ * Copyright(c) 2006 - 2008 Jay Cliburn <jcliburn@gmail.com>
+ * Copyright(c) 2007 Atheros Corporation. All rights reserved.
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ */
+
+/* Including this file like a header is a temporary hack, I promise. -- CHS */
+#ifndef ATLX_C
+#define ATLX_C
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include "atlx.h"
+
+static s32 atlx_read_phy_reg(struct atl1_hw *hw, u16 reg_addr, u16 *phy_data);
+static u32 atlx_hash_mc_addr(struct atl1_hw *hw, u8 *mc_addr);
+static void atlx_set_mac_addr(struct atl1_hw *hw);
+
+static struct atlx_spi_flash_dev flash_table[] = {
+/* MFR_NAME WRSR READ PRGM WREN WRDI RDSR RDID SEC_ERS CHIP_ERS */
+ {"Atmel", 0x00, 0x03, 0x02, 0x06, 0x04, 0x05, 0x15, 0x52, 0x62},
+ {"SST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0x90, 0x20, 0x60},
+ {"ST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0xAB, 0xD8, 0xC7},
+};
+
+static int atlx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ case SIOCGMIIREG:
+ case SIOCSMIIREG:
+ return atlx_mii_ioctl(netdev, ifr, cmd);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/*
+ * atlx_set_mac - Change the Ethernet Address of the NIC
+ * @netdev: network interface device structure
+ * @p: pointer to an address structure
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int atlx_set_mac(struct net_device *netdev, void *p)
+{
+ struct atlx_adapter *adapter = netdev_priv(netdev);
+ struct sockaddr *addr = p;
+
+ if (netif_running(netdev))
+ return -EBUSY;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+ memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len);
+
+ atlx_set_mac_addr(&adapter->hw);
+ return 0;
+}
+
+static void atlx_check_for_link(struct atlx_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ u16 phy_data = 0;
+
+ spin_lock(&adapter->lock);
+ adapter->phy_timer_pending = false;
+ atlx_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
+ atlx_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
+ spin_unlock(&adapter->lock);
+
+ /* notify upper layer link down ASAP */
+ if (!(phy_data & BMSR_LSTATUS)) {
+ /* Link Down */
+ if (netif_carrier_ok(netdev)) {
+ /* old link state: Up */
+ dev_info(&adapter->pdev->dev, "%s link is down\n",
+ netdev->name);
+ adapter->link_speed = SPEED_0;
+ netif_carrier_off(netdev);
+ }
+ }
+ schedule_work(&adapter->link_chg_task);
+}
+
+/*
+ * atlx_set_multi - Multicast and Promiscuous mode set
+ * @netdev: network interface device structure
+ *
+ * The set_multi entry point is called whenever the multicast address
+ * list or the network interface flags are updated. This routine is
+ * responsible for configuring the hardware for proper multicast,
+ * promiscuous mode, and all-multi behavior.
+ */
+static void atlx_set_multi(struct net_device *netdev)
+{
+ struct atlx_adapter *adapter = netdev_priv(netdev);
+ struct atlx_hw *hw = &adapter->hw;
+ struct netdev_hw_addr *ha;
+ u32 rctl;
+ u32 hash_value;
+
+ /* Check for Promiscuous and All Multicast modes */
+ rctl = ioread32(hw->hw_addr + REG_MAC_CTRL);
+ if (netdev->flags & IFF_PROMISC)
+ rctl |= MAC_CTRL_PROMIS_EN;
+ else if (netdev->flags & IFF_ALLMULTI) {
+ rctl |= MAC_CTRL_MC_ALL_EN;
+ rctl &= ~MAC_CTRL_PROMIS_EN;
+ } else
+ rctl &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN);
+
+ iowrite32(rctl, hw->hw_addr + REG_MAC_CTRL);
+
+ /* clear the old settings from the multicast hash table */
+ iowrite32(0, hw->hw_addr + REG_RX_HASH_TABLE);
+ iowrite32(0, (hw->hw_addr + REG_RX_HASH_TABLE) + (1 << 2));
+
+ /* compute mc addresses' hash value ,and put it into hash table */
+ netdev_for_each_mc_addr(ha, netdev) {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ hash_value = atlx_hash_mc_addr(hw, ha->addr);
+#else
+ hash_value = atlx_hash_mc_addr(hw, ha->dmi_addr);
+#endif
+ atlx_hash_set(hw, hash_value);
+ }
+}
+
+/*
+ * atlx_irq_enable - Enable default interrupt generation settings
+ * @adapter: board private structure
+ */
+static void atlx_irq_enable(struct atlx_adapter *adapter)
+{
+ iowrite32(IMR_NORMAL_MASK, adapter->hw.hw_addr + REG_IMR);
+ ioread32(adapter->hw.hw_addr + REG_IMR);
+}
+
+/*
+ * atlx_irq_disable - Mask off interrupt generation on the NIC
+ * @adapter: board private structure
+ */
+static void atlx_irq_disable(struct atlx_adapter *adapter)
+{
+ iowrite32(0, adapter->hw.hw_addr + REG_IMR);
+ ioread32(adapter->hw.hw_addr + REG_IMR);
+ synchronize_irq(adapter->pdev->irq);
+}
+
+static void atlx_clear_phy_int(struct atlx_adapter *adapter)
+{
+ u16 phy_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->lock, flags);
+ atlx_read_phy_reg(&adapter->hw, 19, &phy_data);
+ spin_unlock_irqrestore(&adapter->lock, flags);
+}
+
+/*
+ * atlx_tx_timeout - Respond to a Tx Hang
+ * @netdev: network interface device structure
+ */
+static void atlx_tx_timeout(struct net_device *netdev)
+{
+ struct atlx_adapter *adapter = netdev_priv(netdev);
+ /* Do the reset outside of interrupt context */
+ schedule_work(&adapter->tx_timeout_task);
+}
+
+/*
+ * atlx_link_chg_task - deal with link change event Out of interrupt context
+ */
+static void atlx_link_chg_task(struct work_struct *work)
+{
+ struct atlx_adapter *adapter;
+ unsigned long flags;
+
+ adapter = container_of(work, struct atlx_adapter, link_chg_task);
+
+ spin_lock_irqsave(&adapter->lock, flags);
+ atlx_check_link(adapter);
+ spin_unlock_irqrestore(&adapter->lock, flags);
+}
+
+static void __atlx_vlan_mode(u32 features, u32 *ctrl)
+{
+ if (features & NETIF_F_HW_VLAN_RX) {
+ /* enable VLAN tag insert/strip */
+ *ctrl |= MAC_CTRL_RMV_VLAN;
+ } else {
+ /* disable VLAN tag insert/strip */
+ *ctrl &= ~MAC_CTRL_RMV_VLAN;
+ }
+}
+
+static void atlx_vlan_mode(struct net_device *netdev, u32 features)
+{
+ struct atlx_adapter *adapter = netdev_priv(netdev);
+ unsigned long flags;
+ u32 ctrl;
+
+ spin_lock_irqsave(&adapter->lock, flags);
+ /* atlx_irq_disable(adapter); FIXME: confirm/remove */
+ ctrl = ioread32(adapter->hw.hw_addr + REG_MAC_CTRL);
+ __atlx_vlan_mode(features, &ctrl);
+ iowrite32(ctrl, adapter->hw.hw_addr + REG_MAC_CTRL);
+ /* atlx_irq_enable(adapter); FIXME */
+ spin_unlock_irqrestore(&adapter->lock, flags);
+}
+
+static void atlx_restore_vlan(struct atlx_adapter *adapter)
+{
+ atlx_vlan_mode(adapter->netdev, adapter->netdev->features);
+}
+
+static u32 atlx_fix_features(struct net_device *netdev, u32 features)
+{
+ /*
+ * Since there is no support for separate rx/tx vlan accel
+ * enable/disable make sure tx flag is always in same state as rx.
+ */
+ if (features & NETIF_F_HW_VLAN_RX)
+ features |= NETIF_F_HW_VLAN_TX;
+ else
+ features &= ~NETIF_F_HW_VLAN_TX;
+
+ return features;
+}
+
+static int atlx_set_features(struct net_device *netdev, u32 features)
+{
+ u32 changed = netdev->features ^ features;
+
+ if (changed & NETIF_F_HW_VLAN_RX)
+ atlx_vlan_mode(netdev, features);
+
+ return 0;
+}
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */
+
+#endif /* ATLX_C */
diff --git a/drivers/net/ethernet/atheros/atlx/atlx.h b/drivers/net/ethernet/atheros/atlx/atlx.h
new file mode 100644
index 0000000..14054b7
--- /dev/null
+++ b/drivers/net/ethernet/atheros/atlx/atlx.h
@@ -0,0 +1,503 @@
+/* atlx_hw.h -- common hardware definitions for Attansic network drivers
+ *
+ * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved.
+ * Copyright(c) 2006 - 2007 Chris Snook <csnook@redhat.com>
+ * Copyright(c) 2006 - 2008 Jay Cliburn <jcliburn@gmail.com>
+ * Copyright(c) 2007 Atheros Corporation. All rights reserved.
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. 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 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.
+ */
+
+#ifndef ATLX_H
+#define ATLX_H
+
+#include <linux/module.h>
+#include <linux/types.h>
+
+#define ATLX_ERR_PHY 2
+#define ATLX_ERR_PHY_SPEED 7
+#define ATLX_ERR_PHY_RES 8
+
+#define SPEED_0 0xffff
+#define SPEED_10 10
+#define SPEED_100 100
+#define SPEED_1000 1000
+#define HALF_DUPLEX 1
+#define FULL_DUPLEX 2
+
+#define MEDIA_TYPE_AUTO_SENSOR 0
+
+/* register definitions */
+#define REG_PM_CTRLSTAT 0x44
+
+#define REG_PCIE_CAP_LIST 0x58
+
+#define REG_VPD_CAP 0x6C
+#define VPD_CAP_ID_MASK 0xFF
+#define VPD_CAP_ID_SHIFT 0
+#define VPD_CAP_NEXT_PTR_MASK 0xFF
+#define VPD_CAP_NEXT_PTR_SHIFT 8
+#define VPD_CAP_VPD_ADDR_MASK 0x7FFF
+#define VPD_CAP_VPD_ADDR_SHIFT 16
+#define VPD_CAP_VPD_FLAG 0x80000000
+
+#define REG_VPD_DATA 0x70
+
+#define REG_SPI_FLASH_CTRL 0x200
+#define SPI_FLASH_CTRL_STS_NON_RDY 0x1
+#define SPI_FLASH_CTRL_STS_WEN 0x2
+#define SPI_FLASH_CTRL_STS_WPEN 0x80
+#define SPI_FLASH_CTRL_DEV_STS_MASK 0xFF
+#define SPI_FLASH_CTRL_DEV_STS_SHIFT 0
+#define SPI_FLASH_CTRL_INS_MASK 0x7
+#define SPI_FLASH_CTRL_INS_SHIFT 8
+#define SPI_FLASH_CTRL_START 0x800
+#define SPI_FLASH_CTRL_EN_VPD 0x2000
+#define SPI_FLASH_CTRL_LDSTART 0x8000
+#define SPI_FLASH_CTRL_CS_HI_MASK 0x3
+#define SPI_FLASH_CTRL_CS_HI_SHIFT 16
+#define SPI_FLASH_CTRL_CS_HOLD_MASK 0x3
+#define SPI_FLASH_CTRL_CS_HOLD_SHIFT 18
+#define SPI_FLASH_CTRL_CLK_LO_MASK 0x3
+#define SPI_FLASH_CTRL_CLK_LO_SHIFT 20
+#define SPI_FLASH_CTRL_CLK_HI_MASK 0x3
+#define SPI_FLASH_CTRL_CLK_HI_SHIFT 22
+#define SPI_FLASH_CTRL_CS_SETUP_MASK 0x3
+#define SPI_FLASH_CTRL_CS_SETUP_SHIFT 24
+#define SPI_FLASH_CTRL_EROM_PGSZ_MASK 0x3
+#define SPI_FLASH_CTRL_EROM_PGSZ_SHIFT 26
+#define SPI_FLASH_CTRL_WAIT_READY 0x10000000
+
+#define REG_SPI_ADDR 0x204
+
+#define REG_SPI_DATA 0x208
+
+#define REG_SPI_FLASH_CONFIG 0x20C
+#define SPI_FLASH_CONFIG_LD_ADDR_MASK 0xFFFFFF
+#define SPI_FLASH_CONFIG_LD_ADDR_SHIFT 0
+#define SPI_FLASH_CONFIG_VPD_ADDR_MASK 0x3
+#define SPI_FLASH_CONFIG_VPD_ADDR_SHIFT 24
+#define SPI_FLASH_CONFIG_LD_EXIST 0x4000000
+
+#define REG_SPI_FLASH_OP_PROGRAM 0x210
+#define REG_SPI_FLASH_OP_SC_ERASE 0x211
+#define REG_SPI_FLASH_OP_CHIP_ERASE 0x212
+#define REG_SPI_FLASH_OP_RDID 0x213
+#define REG_SPI_FLASH_OP_WREN 0x214
+#define REG_SPI_FLASH_OP_RDSR 0x215
+#define REG_SPI_FLASH_OP_WRSR 0x216
+#define REG_SPI_FLASH_OP_READ 0x217
+
+#define REG_TWSI_CTRL 0x218
+#define TWSI_CTRL_LD_OFFSET_MASK 0xFF
+#define TWSI_CTRL_LD_OFFSET_SHIFT 0
+#define TWSI_CTRL_LD_SLV_ADDR_MASK 0x7
+#define TWSI_CTRL_LD_SLV_ADDR_SHIFT 8
+#define TWSI_CTRL_SW_LDSTART 0x800
+#define TWSI_CTRL_HW_LDSTART 0x1000
+#define TWSI_CTRL_SMB_SLV_ADDR_MASK 0x7F
+#define TWSI_CTRL_SMB_SLV_ADDR_SHIFT 15
+#define TWSI_CTRL_LD_EXIST 0x400000
+#define TWSI_CTRL_READ_FREQ_SEL_MASK 0x3
+#define TWSI_CTRL_READ_FREQ_SEL_SHIFT 23
+#define TWSI_CTRL_FREQ_SEL_100K 0
+#define TWSI_CTRL_FREQ_SEL_200K 1
+#define TWSI_CTRL_FREQ_SEL_300K 2
+#define TWSI_CTRL_FREQ_SEL_400K 3
+#define TWSI_CTRL_SMB_SLV_ADDR /* FIXME: define or remove */
+#define TWSI_CTRL_WRITE_FREQ_SEL_MASK 0x3
+#define TWSI_CTRL_WRITE_FREQ_SEL_SHIFT 24
+
+#define REG_PCIE_DEV_MISC_CTRL 0x21C
+#define PCIE_DEV_MISC_CTRL_EXT_PIPE 0x2
+#define PCIE_DEV_MISC_CTRL_RETRY_BUFDIS 0x1
+#define PCIE_DEV_MISC_CTRL_SPIROM_EXIST 0x4
+#define PCIE_DEV_MISC_CTRL_SERDES_ENDIAN 0x8
+#define PCIE_DEV_MISC_CTRL_SERDES_SEL_DIN 0x10
+
+#define REG_PCIE_PHYMISC 0x1000
+#define PCIE_PHYMISC_FORCE_RCV_DET 0x4
+
+#define REG_PCIE_DLL_TX_CTRL1 0x1104
+#define PCIE_DLL_TX_CTRL1_SEL_NOR_CLK 0x400
+#define PCIE_DLL_TX_CTRL1_DEF 0x568
+
+#define REG_LTSSM_TEST_MODE 0x12FC
+#define LTSSM_TEST_MODE_DEF 0x6500
+
+/* Master Control Register */
+#define REG_MASTER_CTRL 0x1400
+#define MASTER_CTRL_SOFT_RST 0x1
+#define MASTER_CTRL_MTIMER_EN 0x2
+#define MASTER_CTRL_ITIMER_EN 0x4
+#define MASTER_CTRL_MANUAL_INT 0x8
+#define MASTER_CTRL_REV_NUM_SHIFT 16
+#define MASTER_CTRL_REV_NUM_MASK 0xFF
+#define MASTER_CTRL_DEV_ID_SHIFT 24
+#define MASTER_CTRL_DEV_ID_MASK 0xFF
+
+/* Timer Initial Value Register */
+#define REG_MANUAL_TIMER_INIT 0x1404
+
+/* IRQ Moderator Timer Initial Value Register */
+#define REG_IRQ_MODU_TIMER_INIT 0x1408
+
+#define REG_PHY_ENABLE 0x140C
+
+/* IRQ Anti-Lost Timer Initial Value Register */
+#define REG_CMBDISDMA_TIMER 0x140E
+
+/* Block IDLE Status Register */
+#define REG_IDLE_STATUS 0x1410
+
+/* MDIO Control Register */
+#define REG_MDIO_CTRL 0x1414
+#define MDIO_DATA_MASK 0xFFFF
+#define MDIO_DATA_SHIFT 0
+#define MDIO_REG_ADDR_MASK 0x1F
+#define MDIO_REG_ADDR_SHIFT 16
+#define MDIO_RW 0x200000
+#define MDIO_SUP_PREAMBLE 0x400000
+#define MDIO_START 0x800000
+#define MDIO_CLK_SEL_SHIFT 24
+#define MDIO_CLK_25_4 0
+#define MDIO_CLK_25_6 2
+#define MDIO_CLK_25_8 3
+#define MDIO_CLK_25_10 4
+#define MDIO_CLK_25_14 5
+#define MDIO_CLK_25_20 6
+#define MDIO_CLK_25_28 7
+#define MDIO_BUSY 0x8000000
+
+/* MII PHY Status Register */
+#define REG_PHY_STATUS 0x1418
+
+/* BIST Control and Status Register0 (for the Packet Memory) */
+#define REG_BIST0_CTRL 0x141C
+#define BIST0_NOW 0x1
+#define BIST0_SRAM_FAIL 0x2
+#define BIST0_FUSE_FLAG 0x4
+#define REG_BIST1_CTRL 0x1420
+#define BIST1_NOW 0x1
+#define BIST1_SRAM_FAIL 0x2
+#define BIST1_FUSE_FLAG 0x4
+
+/* SerDes Lock Detect Control and Status Register */
+#define REG_SERDES_LOCK 0x1424
+#define SERDES_LOCK_DETECT 1
+#define SERDES_LOCK_DETECT_EN 2
+
+/* MAC Control Register */
+#define REG_MAC_CTRL 0x1480
+#define MAC_CTRL_TX_EN 1
+#define MAC_CTRL_RX_EN 2
+#define MAC_CTRL_TX_FLOW 4
+#define MAC_CTRL_RX_FLOW 8
+#define MAC_CTRL_LOOPBACK 0x10
+#define MAC_CTRL_DUPLX 0x20
+#define MAC_CTRL_ADD_CRC 0x40
+#define MAC_CTRL_PAD 0x80
+#define MAC_CTRL_LENCHK 0x100
+#define MAC_CTRL_HUGE_EN 0x200
+#define MAC_CTRL_PRMLEN_SHIFT 10
+#define MAC_CTRL_PRMLEN_MASK 0xF
+#define MAC_CTRL_RMV_VLAN 0x4000
+#define MAC_CTRL_PROMIS_EN 0x8000
+#define MAC_CTRL_MC_ALL_EN 0x2000000
+#define MAC_CTRL_BC_EN 0x4000000
+
+/* MAC IPG/IFG Control Register */
+#define REG_MAC_IPG_IFG 0x1484
+#define MAC_IPG_IFG_IPGT_SHIFT 0
+#define MAC_IPG_IFG_IPGT_MASK 0x7F
+#define MAC_IPG_IFG_MIFG_SHIFT 8
+#define MAC_IPG_IFG_MIFG_MASK 0xFF
+#define MAC_IPG_IFG_IPGR1_SHIFT 16
+#define MAC_IPG_IFG_IPGR1_MASK 0x7F
+#define MAC_IPG_IFG_IPGR2_SHIFT 24
+#define MAC_IPG_IFG_IPGR2_MASK 0x7F
+
+/* MAC STATION ADDRESS */
+#define REG_MAC_STA_ADDR 0x1488
+
+/* Hash table for multicast address */
+#define REG_RX_HASH_TABLE 0x1490
+
+/* MAC Half-Duplex Control Register */
+#define REG_MAC_HALF_DUPLX_CTRL 0x1498
+#define MAC_HALF_DUPLX_CTRL_LCOL_SHIFT 0
+#define MAC_HALF_DUPLX_CTRL_LCOL_MASK 0x3FF
+#define MAC_HALF_DUPLX_CTRL_RETRY_SHIFT 12
+#define MAC_HALF_DUPLX_CTRL_RETRY_MASK 0xF
+#define MAC_HALF_DUPLX_CTRL_EXC_DEF_EN 0x10000
+#define MAC_HALF_DUPLX_CTRL_NO_BACK_C 0x20000
+#define MAC_HALF_DUPLX_CTRL_NO_BACK_P 0x40000
+#define MAC_HALF_DUPLX_CTRL_ABEBE 0x80000
+#define MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT 20
+#define MAC_HALF_DUPLX_CTRL_ABEBT_MASK 0xF
+#define MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT 24
+#define MAC_HALF_DUPLX_CTRL_JAMIPG_MASK 0xF
+
+/* Maximum Frame Length Control Register */
+#define REG_MTU 0x149C
+
+/* Wake-On-Lan control register */
+#define REG_WOL_CTRL 0x14A0
+#define WOL_PATTERN_EN 0x1
+#define WOL_PATTERN_PME_EN 0x2
+#define WOL_MAGIC_EN 0x4
+#define WOL_MAGIC_PME_EN 0x8
+#define WOL_LINK_CHG_EN 0x10
+#define WOL_LINK_CHG_PME_EN 0x20
+#define WOL_PATTERN_ST 0x100
+#define WOL_MAGIC_ST 0x200
+#define WOL_LINKCHG_ST 0x400
+#define WOL_PT0_EN 0x10000
+#define WOL_PT1_EN 0x20000
+#define WOL_PT2_EN 0x40000
+#define WOL_PT3_EN 0x80000
+#define WOL_PT4_EN 0x100000
+#define WOL_PT0_MATCH 0x1000000
+#define WOL_PT1_MATCH 0x2000000
+#define WOL_PT2_MATCH 0x4000000
+#define WOL_PT3_MATCH 0x8000000
+#define WOL_PT4_MATCH 0x10000000
+
+/* Internal SRAM Partition Register, high 32 bits */
+#define REG_SRAM_RFD_ADDR 0x1500
+
+/* Descriptor Control register, high 32 bits */
+#define REG_DESC_BASE_ADDR_HI 0x1540
+
+/* Interrupt Status Register */
+#define REG_ISR 0x1600
+#define ISR_UR_DETECTED 0x1000000
+#define ISR_FERR_DETECTED 0x2000000
+#define ISR_NFERR_DETECTED 0x4000000
+#define ISR_CERR_DETECTED 0x8000000
+#define ISR_PHY_LINKDOWN 0x10000000
+#define ISR_DIS_INT 0x80000000
+
+/* Interrupt Mask Register */
+#define REG_IMR 0x1604
+
+#define REG_RFD_RRD_IDX 0x1800
+#define REG_TPD_IDX 0x1804
+
+/* MII definitions */
+
+/* PHY Common Register */
+#define MII_ATLX_CR 0x09
+#define MII_ATLX_SR 0x0A
+#define MII_ATLX_ESR 0x0F
+#define MII_ATLX_PSCR 0x10
+#define MII_ATLX_PSSR 0x11
+
+/* PHY Control Register */
+#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100,
+ * 00=10
+ */
+#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */
+#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */
+#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */
+#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */
+#define MII_CR_POWER_DOWN 0x0800 /* Power down */
+#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */
+#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100,
+ * 00=10
+ */
+#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */
+#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */
+#define MII_CR_SPEED_MASK 0x2040
+#define MII_CR_SPEED_1000 0x0040
+#define MII_CR_SPEED_100 0x2000
+#define MII_CR_SPEED_10 0x0000
+
+/* PHY Status Register */
+#define MII_SR_EXTENDED_CAPS 0x0001 /* Ext register capabilities */
+#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */
+#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */
+#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */
+#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */
+#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */
+#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */
+#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext stat info in Reg 0x0F */
+#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */
+#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */
+#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */
+#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */
+#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */
+#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */
+#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */
+
+/* Link partner ability register */
+#define MII_LPA_SLCT 0x001f /* Same as advertise selector */
+#define MII_LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */
+#define MII_LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */
+#define MII_LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */
+#define MII_LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */
+#define MII_LPA_100BASE4 0x0200 /* 100BASE-T4 */
+#define MII_LPA_PAUSE 0x0400 /* PAUSE */
+#define MII_LPA_ASYPAUSE 0x0800 /* Asymmetrical PAUSE */
+#define MII_LPA_RFAULT 0x2000 /* Link partner faulted */
+#define MII_LPA_LPACK 0x4000 /* Link partner acked us */
+#define MII_LPA_NPAGE 0x8000 /* Next page bit */
+
+/* Autoneg Advertisement Register */
+#define MII_AR_SELECTOR_FIELD 0x0001 /* IEEE 802.3 CSMA/CD */
+#define MII_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */
+#define MII_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */
+#define MII_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */
+#define MII_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */
+#define MII_AR_100T4_CAPS 0x0200 /* 100T4 Capable */
+#define MII_AR_PAUSE 0x0400 /* Pause operation desired */
+#define MII_AR_ASM_DIR 0x0800 /* Asymmetric Pause Dir bit */
+#define MII_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */
+#define MII_AR_NEXT_PAGE 0x8000 /* Next Page ability support */
+#define MII_AR_SPEED_MASK 0x01E0
+#define MII_AR_DEFAULT_CAP_MASK 0x0DE0
+
+/* 1000BASE-T Control Register */
+#define MII_ATLX_CR_1000T_HD_CAPS 0x0100 /* Adv 1000T HD cap */
+#define MII_ATLX_CR_1000T_FD_CAPS 0x0200 /* Adv 1000T FD cap */
+#define MII_ATLX_CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device,
+ * 0=DTE device */
+#define MII_ATLX_CR_1000T_MS_VALUE 0x0800 /* 1=Config PHY as Master,
+ * 0=Configure PHY as Slave */
+#define MII_ATLX_CR_1000T_MS_ENABLE 0x1000 /* 1=Man Master/Slave config,
+ * 0=Auto Master/Slave config
+ */
+#define MII_ATLX_CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */
+#define MII_ATLX_CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */
+#define MII_ATLX_CR_1000T_TEST_MODE_2 0x4000 /* Master Xmit Jitter test */
+#define MII_ATLX_CR_1000T_TEST_MODE_3 0x6000 /* Slave Xmit Jitter test */
+#define MII_ATLX_CR_1000T_TEST_MODE_4 0x8000 /* Xmitter Distortion test */
+#define MII_ATLX_CR_1000T_SPEED_MASK 0x0300
+#define MII_ATLX_CR_1000T_DEFAULT_CAP_MASK 0x0300
+
+/* 1000BASE-T Status Register */
+#define MII_ATLX_SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */
+#define MII_ATLX_SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */
+#define MII_ATLX_SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */
+#define MII_ATLX_SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */
+#define MII_ATLX_SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master
+ * 0=Slave
+ */
+#define MII_ATLX_SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config
+ * fault */
+#define MII_ATLX_SR_1000T_REMOTE_RX_STATUS_SHIFT 12
+#define MII_ATLX_SR_1000T_LOCAL_RX_STATUS_SHIFT 13
+
+/* Extended Status Register */
+#define MII_ATLX_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */
+#define MII_ATLX_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */
+#define MII_ATLX_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */
+#define MII_ATLX_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */
+
+/* ATLX PHY Specific Control Register */
+#define MII_ATLX_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Func disabled */
+#define MII_ATLX_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enbld */
+#define MII_ATLX_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */
+#define MII_ATLX_PSCR_MAC_POWERDOWN 0x0008
+#define MII_ATLX_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low
+ * 0=CLK125 toggling
+ */
+#define MII_ATLX_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5,
+ * Manual MDI configuration
+ */
+#define MII_ATLX_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */
+#define MII_ATLX_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover
+ * 100BASE-TX/10BASE-T: MDI
+ * Mode */
+#define MII_ATLX_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled
+ * all speeds.
+ */
+#define MII_ATLX_PSCR_10BT_EXT_DIST_ENABLE 0x0080 /* 1=Enable Extended
+ * 10BASE-T distance
+ * (Lower 10BASE-T RX
+ * Threshold)
+ * 0=Normal 10BASE-T RX
+ * Threshold
+ */
+#define MII_ATLX_PSCR_MII_5BIT_ENABLE 0x0100 /* 1=5-Bit interface in
+ * 100BASE-TX
+ * 0=MII interface in
+ * 100BASE-TX
+ */
+#define MII_ATLX_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler dsbl */
+#define MII_ATLX_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */
+#define MII_ATLX_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */
+#define MII_ATLX_PSCR_POLARITY_REVERSAL_SHIFT 1
+#define MII_ATLX_PSCR_AUTO_X_MODE_SHIFT 5
+#define MII_ATLX_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7
+
+/* ATLX PHY Specific Status Register */
+#define MII_ATLX_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */
+#define MII_ATLX_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */
+#define MII_ATLX_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */
+#define MII_ATLX_PSSR_10MBS 0x0000 /* 00=10Mbs */
+#define MII_ATLX_PSSR_100MBS 0x4000 /* 01=100Mbs */
+#define MII_ATLX_PSSR_1000MBS 0x8000 /* 10=1000Mbs */
+
+#define MII_DBG_ADDR 0x1D
+#define MII_DBG_DATA 0x1E
+
+/* PCI Command Register Bit Definitions */
+#define PCI_REG_COMMAND 0x04 /* PCI Command Register */
+#define CMD_IO_SPACE 0x0001
+#define CMD_MEMORY_SPACE 0x0002
+#define CMD_BUS_MASTER 0x0004
+
+/* Wake Up Filter Control */
+#define ATLX_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */
+#define ATLX_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */
+#define ATLX_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */
+#define ATLX_WUFC_MC 0x00000008 /* Multicast Wakeup Enable */
+#define ATLX_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */
+
+#define ADVERTISE_10_HALF 0x0001
+#define ADVERTISE_10_FULL 0x0002
+#define ADVERTISE_100_HALF 0x0004
+#define ADVERTISE_100_FULL 0x0008
+#define ADVERTISE_1000_HALF 0x0010
+#define ADVERTISE_1000_FULL 0x0020
+#define AUTONEG_ADVERTISE_10_100_ALL 0x000F /* All 10/100 speeds */
+#define AUTONEG_ADVERTISE_10_ALL 0x0003 /* 10Mbps Full & Half speeds */
+
+#define PHY_AUTO_NEG_TIME 45 /* 4.5 Seconds */
+#define PHY_FORCE_TIME 20 /* 2.0 Seconds */
+
+/* For checksumming, the sum of all words in the EEPROM should equal 0xBABA */
+#define EEPROM_SUM 0xBABA
+#define NODE_ADDRESS_SIZE 6
+
+struct atlx_spi_flash_dev {
+ const char *manu_name; /* manufacturer id */
+ /* op-code */
+ u8 cmd_wrsr;
+ u8 cmd_read;
+ u8 cmd_program;
+ u8 cmd_wren;
+ u8 cmd_wrdi;
+ u8 cmd_rdsr;
+ u8 cmd_rdid;
+ u8 cmd_sector_erase;
+ u8 cmd_chip_erase;
+};
+
+#endif /* ATLX_H */
diff --git a/drivers/net/ethernet/broadcom/Makefile b/drivers/net/ethernet/broadcom/Makefile
new file mode 100644
index 0000000..b777846
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/Makefile
@@ -0,0 +1,2 @@
+
+obj-$(CONFIG_B44) += b44.o
diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c
new file mode 100644
index 0000000..1f3645e
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -0,0 +1,2378 @@
+/* b44.c: Broadcom 44xx/47xx Fast Ethernet device driver.
+ *
+ * Copyright (C) 2002 David S. Miller (davem@redhat.com)
+ * Copyright (C) 2004 Pekka Pietikainen (pp@ee.oulu.fi)
+ * Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org)
+ * Copyright (C) 2006 Felix Fietkau (nbd@openwrt.org)
+ * Copyright (C) 2006 Broadcom Corporation.
+ * Copyright (C) 2007 Michael Buesch <m@bues.ch>
+ *
+ * Distribute under GPL.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/etherdevice.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/ssb/ssb.h>
+#include <linux/slab.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+
+#include "b44.h"
+
+#define DRV_MODULE_NAME "b44"
+#define DRV_MODULE_VERSION "2.0"
+#define DRV_DESCRIPTION "Broadcom 44xx/47xx 10/100 PCI ethernet driver"
+
+#define B44_DEF_MSG_ENABLE \
+ (NETIF_MSG_DRV | \
+ NETIF_MSG_PROBE | \
+ NETIF_MSG_LINK | \
+ NETIF_MSG_TIMER | \
+ NETIF_MSG_IFDOWN | \
+ NETIF_MSG_IFUP | \
+ NETIF_MSG_RX_ERR | \
+ NETIF_MSG_TX_ERR)
+
+/* length of time before we decide the hardware is borked,
+ * and dev->tx_timeout() should be called to fix the problem
+ */
+#define B44_TX_TIMEOUT (5 * HZ)
+
+/* hardware minimum and maximum for a single frame's data payload */
+#define B44_MIN_MTU 60
+#define B44_MAX_MTU 1500
+
+#define B44_RX_RING_SIZE 512
+#define B44_DEF_RX_RING_PENDING 200
+#define B44_RX_RING_BYTES (sizeof(struct dma_desc) * \
+ B44_RX_RING_SIZE)
+#define B44_TX_RING_SIZE 512
+#define B44_DEF_TX_RING_PENDING (B44_TX_RING_SIZE - 1)
+#define B44_TX_RING_BYTES (sizeof(struct dma_desc) * \
+ B44_TX_RING_SIZE)
+
+#define TX_RING_GAP(BP) \
+ (B44_TX_RING_SIZE - (BP)->tx_pending)
+#define TX_BUFFS_AVAIL(BP) \
+ (((BP)->tx_cons <= (BP)->tx_prod) ? \
+ (BP)->tx_cons + (BP)->tx_pending - (BP)->tx_prod : \
+ (BP)->tx_cons - (BP)->tx_prod - TX_RING_GAP(BP))
+#define NEXT_TX(N) (((N) + 1) & (B44_TX_RING_SIZE - 1))
+
+#define RX_PKT_OFFSET (RX_HEADER_LEN + 2)
+#define RX_PKT_BUF_SZ (1536 + RX_PKT_OFFSET)
+
+/* minimum number of free TX descriptors required to wake up TX process */
+#define B44_TX_WAKEUP_THRESH (B44_TX_RING_SIZE / 4)
+
+/* b44 internal pattern match filter info */
+#define B44_PATTERN_BASE 0x400
+#define B44_PATTERN_SIZE 0x80
+#define B44_PMASK_BASE 0x600
+#define B44_PMASK_SIZE 0x10
+#define B44_MAX_PATTERNS 16
+#define B44_ETHIPV6UDP_HLEN 62
+#define B44_ETHIPV4UDP_HLEN 42
+
+MODULE_AUTHOR("Felix Fietkau, Florian Schirmer, Pekka Pietikainen, David S. Miller");
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+static int b44_debug = -1; /* -1 == use B44_DEF_MSG_ENABLE as value */
+module_param(b44_debug, int, 0);
+MODULE_PARM_DESC(b44_debug, "B44 bitmapped debugging message enable value");
+
+
+#ifdef CONFIG_B44_PCI
+static DEFINE_PCI_DEVICE_TABLE(b44_pci_tbl) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B1) },
+ { 0 } /* terminate list with empty entry */
+};
+MODULE_DEVICE_TABLE(pci, b44_pci_tbl);
+
+static struct pci_driver b44_pci_driver = {
+ .name = DRV_MODULE_NAME,
+ .id_table = b44_pci_tbl,
+};
+#endif /* CONFIG_B44_PCI */
+
+static const struct ssb_device_id b44_ssb_tbl[] = {
+ SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_ETHERNET, SSB_ANY_REV),
+ SSB_DEVTABLE_END
+};
+MODULE_DEVICE_TABLE(ssb, b44_ssb_tbl);
+
+static void b44_halt(struct b44 *);
+static void b44_init_rings(struct b44 *);
+
+#define B44_FULL_RESET 1
+#define B44_FULL_RESET_SKIP_PHY 2
+#define B44_PARTIAL_RESET 3
+#define B44_CHIP_RESET_FULL 4
+#define B44_CHIP_RESET_PARTIAL 5
+
+static void b44_init_hw(struct b44 *, int);
+
+static int dma_desc_sync_size;
+static int instance;
+
+static const char b44_gstrings[][ETH_GSTRING_LEN] = {
+#define _B44(x...) # x,
+B44_STAT_REG_DECLARE
+#undef _B44
+};
+
+static inline void b44_sync_dma_desc_for_device(struct ssb_device *sdev,
+ dma_addr_t dma_base,
+ unsigned long offset,
+ enum dma_data_direction dir)
+{
+ dma_sync_single_for_device(sdev->dma_dev, dma_base + offset,
+ dma_desc_sync_size, dir);
+}
+
+static inline void b44_sync_dma_desc_for_cpu(struct ssb_device *sdev,
+ dma_addr_t dma_base,
+ unsigned long offset,
+ enum dma_data_direction dir)
+{
+ dma_sync_single_for_cpu(sdev->dma_dev, dma_base + offset,
+ dma_desc_sync_size, dir);
+}
+
+static inline unsigned long br32(const struct b44 *bp, unsigned long reg)
+{
+ return ssb_read32(bp->sdev, reg);
+}
+
+static inline void bw32(const struct b44 *bp,
+ unsigned long reg, unsigned long val)
+{
+ ssb_write32(bp->sdev, reg, val);
+}
+
+static int b44_wait_bit(struct b44 *bp, unsigned long reg,
+ u32 bit, unsigned long timeout, const int clear)
+{
+ unsigned long i;
+
+ for (i = 0; i < timeout; i++) {
+ u32 val = br32(bp, reg);
+
+ if (clear && !(val & bit))
+ break;
+ if (!clear && (val & bit))
+ break;
+ udelay(10);
+ }
+ if (i == timeout) {
+ if (net_ratelimit())
+ netdev_err(bp->dev, "BUG! Timeout waiting for bit %08x of register %lx to %s\n",
+ bit, reg, clear ? "clear" : "set");
+
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static inline void __b44_cam_read(struct b44 *bp, unsigned char *data, int index)
+{
+ u32 val;
+
+ bw32(bp, B44_CAM_CTRL, (CAM_CTRL_READ |
+ (index << CAM_CTRL_INDEX_SHIFT)));
+
+ b44_wait_bit(bp, B44_CAM_CTRL, CAM_CTRL_BUSY, 100, 1);
+
+ val = br32(bp, B44_CAM_DATA_LO);
+
+ data[2] = (val >> 24) & 0xFF;
+ data[3] = (val >> 16) & 0xFF;
+ data[4] = (val >> 8) & 0xFF;
+ data[5] = (val >> 0) & 0xFF;
+
+ val = br32(bp, B44_CAM_DATA_HI);
+
+ data[0] = (val >> 8) & 0xFF;
+ data[1] = (val >> 0) & 0xFF;
+}
+
+static inline void __b44_cam_write(struct b44 *bp, unsigned char *data, int index)
+{
+ u32 val;
+
+ val = ((u32) data[2]) << 24;
+ val |= ((u32) data[3]) << 16;
+ val |= ((u32) data[4]) << 8;
+ val |= ((u32) data[5]) << 0;
+ bw32(bp, B44_CAM_DATA_LO, val);
+ val = (CAM_DATA_HI_VALID |
+ (((u32) data[0]) << 8) |
+ (((u32) data[1]) << 0));
+ bw32(bp, B44_CAM_DATA_HI, val);
+ bw32(bp, B44_CAM_CTRL, (CAM_CTRL_WRITE |
+ (index << CAM_CTRL_INDEX_SHIFT)));
+ b44_wait_bit(bp, B44_CAM_CTRL, CAM_CTRL_BUSY, 100, 1);
+}
+
+static inline void __b44_disable_ints(struct b44 *bp)
+{
+ bw32(bp, B44_IMASK, 0);
+}
+
+static void b44_disable_ints(struct b44 *bp)
+{
+ __b44_disable_ints(bp);
+
+ /* Flush posted writes. */
+ br32(bp, B44_IMASK);
+}
+
+static void b44_enable_ints(struct b44 *bp)
+{
+ bw32(bp, B44_IMASK, bp->imask);
+}
+
+static int __b44_readphy(struct b44 *bp, int phy_addr, int reg, u32 *val)
+{
+ int err;
+
+ bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII);
+ bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START |
+ (MDIO_OP_READ << MDIO_DATA_OP_SHIFT) |
+ (phy_addr << MDIO_DATA_PMD_SHIFT) |
+ (reg << MDIO_DATA_RA_SHIFT) |
+ (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT)));
+ err = b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0);
+ *val = br32(bp, B44_MDIO_DATA) & MDIO_DATA_DATA;
+
+ return err;
+}
+
+static int __b44_writephy(struct b44 *bp, int phy_addr, int reg, u32 val)
+{
+ bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII);
+ bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START |
+ (MDIO_OP_WRITE << MDIO_DATA_OP_SHIFT) |
+ (phy_addr << MDIO_DATA_PMD_SHIFT) |
+ (reg << MDIO_DATA_RA_SHIFT) |
+ (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT) |
+ (val & MDIO_DATA_DATA)));
+ return b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0);
+}
+
+static inline int b44_readphy(struct b44 *bp, int reg, u32 *val)
+{
+ if (bp->phy_addr == B44_PHY_ADDR_NO_PHY)
+ return 0;
+
+ return __b44_readphy(bp, bp->phy_addr, reg, val);
+}
+
+static inline int b44_writephy(struct b44 *bp, int reg, u32 val)
+{
+ if (bp->phy_addr == B44_PHY_ADDR_NO_PHY)
+ return 0;
+
+ return __b44_writephy(bp, bp->phy_addr, reg, val);
+}
+
+/* miilib interface */
+static int b44_mii_read(struct net_device *dev, int phy_id, int location)
+{
+ u32 val;
+ struct b44 *bp = netdev_priv(dev);
+ int rc = __b44_readphy(bp, phy_id, location, &val);
+ if (rc)
+ return 0xffffffff;
+ return val;
+}
+
+static void b44_mii_write(struct net_device *dev, int phy_id, int location,
+ int val)
+{
+ struct b44 *bp = netdev_priv(dev);
+ __b44_writephy(bp, phy_id, location, val);
+}
+
+static int b44_phy_reset(struct b44 *bp)
+{
+ u32 val;
+ int err;
+
+ if (bp->phy_addr == B44_PHY_ADDR_NO_PHY)
+ return 0;
+ err = b44_writephy(bp, MII_BMCR, BMCR_RESET);
+ if (err)
+ return err;
+ udelay(100);
+ err = b44_readphy(bp, MII_BMCR, &val);
+ if (!err) {
+ if (val & BMCR_RESET) {
+ netdev_err(bp->dev, "PHY Reset would not complete\n");
+ err = -ENODEV;
+ }
+ }
+
+ return err;
+}
+
+static void __b44_set_flow_ctrl(struct b44 *bp, u32 pause_flags)
+{
+ u32 val;
+
+ bp->flags &= ~(B44_FLAG_TX_PAUSE | B44_FLAG_RX_PAUSE);
+ bp->flags |= pause_flags;
+
+ val = br32(bp, B44_RXCONFIG);
+ if (pause_flags & B44_FLAG_RX_PAUSE)
+ val |= RXCONFIG_FLOW;
+ else
+ val &= ~RXCONFIG_FLOW;
+ bw32(bp, B44_RXCONFIG, val);
+
+ val = br32(bp, B44_MAC_FLOW);
+ if (pause_flags & B44_FLAG_TX_PAUSE)
+ val |= (MAC_FLOW_PAUSE_ENAB |
+ (0xc0 & MAC_FLOW_RX_HI_WATER));
+ else
+ val &= ~MAC_FLOW_PAUSE_ENAB;
+ bw32(bp, B44_MAC_FLOW, val);
+}
+
+static void b44_set_flow_ctrl(struct b44 *bp, u32 local, u32 remote)
+{
+ u32 pause_enab = 0;
+
+ /* The driver supports only rx pause by default because
+ the b44 mac tx pause mechanism generates excessive
+ pause frames.
+ Use ethtool to turn on b44 tx pause if necessary.
+ */
+ if ((local & ADVERTISE_PAUSE_CAP) &&
+ (local & ADVERTISE_PAUSE_ASYM)){
+ if ((remote & LPA_PAUSE_ASYM) &&
+ !(remote & LPA_PAUSE_CAP))
+ pause_enab |= B44_FLAG_RX_PAUSE;
+ }
+
+ __b44_set_flow_ctrl(bp, pause_enab);
+}
+
+#ifdef CONFIG_BCM47XX
+#include <asm/mach-bcm47xx/nvram.h>
+static void b44_wap54g10_workaround(struct b44 *bp)
+{
+ char buf[20];
+ u32 val;
+ int err;
+
+ /*
+ * workaround for bad hardware design in Linksys WAP54G v1.0
+ * see https://dev.openwrt.org/ticket/146
+ * check and reset bit "isolate"
+ */
+ if (nvram_getenv("boardnum", buf, sizeof(buf)) < 0)
+ return;
+ if (simple_strtoul(buf, NULL, 0) == 2) {
+ err = __b44_readphy(bp, 0, MII_BMCR, &val);
+ if (err)
+ goto error;
+ if (!(val & BMCR_ISOLATE))
+ return;
+ val &= ~BMCR_ISOLATE;
+ err = __b44_writephy(bp, 0, MII_BMCR, val);
+ if (err)
+ goto error;
+ }
+ return;
+error:
+ pr_warning("PHY: cannot reset MII transceiver isolate bit\n");
+}
+#else
+static inline void b44_wap54g10_workaround(struct b44 *bp)
+{
+}
+#endif
+
+static int b44_setup_phy(struct b44 *bp)
+{
+ u32 val;
+ int err;
+
+ b44_wap54g10_workaround(bp);
+
+ if (bp->phy_addr == B44_PHY_ADDR_NO_PHY)
+ return 0;
+ if ((err = b44_readphy(bp, B44_MII_ALEDCTRL, &val)) != 0)
+ goto out;
+ if ((err = b44_writephy(bp, B44_MII_ALEDCTRL,
+ val & MII_ALEDCTRL_ALLMSK)) != 0)
+ goto out;
+ if ((err = b44_readphy(bp, B44_MII_TLEDCTRL, &val)) != 0)
+ goto out;
+ if ((err = b44_writephy(bp, B44_MII_TLEDCTRL,
+ val | MII_TLEDCTRL_ENABLE)) != 0)
+ goto out;
+
+ if (!(bp->flags & B44_FLAG_FORCE_LINK)) {
+ u32 adv = ADVERTISE_CSMA;
+
+ if (bp->flags & B44_FLAG_ADV_10HALF)
+ adv |= ADVERTISE_10HALF;
+ if (bp->flags & B44_FLAG_ADV_10FULL)
+ adv |= ADVERTISE_10FULL;
+ if (bp->flags & B44_FLAG_ADV_100HALF)
+ adv |= ADVERTISE_100HALF;
+ if (bp->flags & B44_FLAG_ADV_100FULL)
+ adv |= ADVERTISE_100FULL;
+
+ if (bp->flags & B44_FLAG_PAUSE_AUTO)
+ adv |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
+
+ if ((err = b44_writephy(bp, MII_ADVERTISE, adv)) != 0)
+ goto out;
+ if ((err = b44_writephy(bp, MII_BMCR, (BMCR_ANENABLE |
+ BMCR_ANRESTART))) != 0)
+ goto out;
+ } else {
+ u32 bmcr;
+
+ if ((err = b44_readphy(bp, MII_BMCR, &bmcr)) != 0)
+ goto out;
+ bmcr &= ~(BMCR_FULLDPLX | BMCR_ANENABLE | BMCR_SPEED100);
+ if (bp->flags & B44_FLAG_100_BASE_T)
+ bmcr |= BMCR_SPEED100;
+ if (bp->flags & B44_FLAG_FULL_DUPLEX)
+ bmcr |= BMCR_FULLDPLX;
+ if ((err = b44_writephy(bp, MII_BMCR, bmcr)) != 0)
+ goto out;
+
+ /* Since we will not be negotiating there is no safe way
+ * to determine if the link partner supports flow control
+ * or not. So just disable it completely in this case.
+ */
+ b44_set_flow_ctrl(bp, 0, 0);
+ }
+
+out:
+ return err;
+}
+
+static void b44_stats_update(struct b44 *bp)
+{
+ unsigned long reg;
+ u32 *val;
+
+ val = &bp->hw_stats.tx_good_octets;
+ for (reg = B44_TX_GOOD_O; reg <= B44_TX_PAUSE; reg += 4UL) {
+ *val++ += br32(bp, reg);
+ }
+
+ /* Pad */
+ reg += 8*4UL;
+
+ for (reg = B44_RX_GOOD_O; reg <= B44_RX_NPAUSE; reg += 4UL) {
+ *val++ += br32(bp, reg);
+ }
+}
+
+static void b44_link_report(struct b44 *bp)
+{
+ if (!netif_carrier_ok(bp->dev)) {
+ netdev_info(bp->dev, "Link is down\n");
+ } else {
+ netdev_info(bp->dev, "Link is up at %d Mbps, %s duplex\n",
+ (bp->flags & B44_FLAG_100_BASE_T) ? 100 : 10,
+ (bp->flags & B44_FLAG_FULL_DUPLEX) ? "full" : "half");
+
+ netdev_info(bp->dev, "Flow control is %s for TX and %s for RX\n",
+ (bp->flags & B44_FLAG_TX_PAUSE) ? "on" : "off",
+ (bp->flags & B44_FLAG_RX_PAUSE) ? "on" : "off");
+ }
+}
+
+static void b44_check_phy(struct b44 *bp)
+{
+ u32 bmsr, aux;
+
+ if (bp->phy_addr == B44_PHY_ADDR_NO_PHY) {
+ bp->flags |= B44_FLAG_100_BASE_T;
+ bp->flags |= B44_FLAG_FULL_DUPLEX;
+ if (!netif_carrier_ok(bp->dev)) {
+ u32 val = br32(bp, B44_TX_CTRL);
+ val |= TX_CTRL_DUPLEX;
+ bw32(bp, B44_TX_CTRL, val);
+ netif_carrier_on(bp->dev);
+ b44_link_report(bp);
+ }
+ return;
+ }
+
+ if (!b44_readphy(bp, MII_BMSR, &bmsr) &&
+ !b44_readphy(bp, B44_MII_AUXCTRL, &aux) &&
+ (bmsr != 0xffff)) {
+ if (aux & MII_AUXCTRL_SPEED)
+ bp->flags |= B44_FLAG_100_BASE_T;
+ else
+ bp->flags &= ~B44_FLAG_100_BASE_T;
+ if (aux & MII_AUXCTRL_DUPLEX)
+ bp->flags |= B44_FLAG_FULL_DUPLEX;
+ else
+ bp->flags &= ~B44_FLAG_FULL_DUPLEX;
+
+ if (!netif_carrier_ok(bp->dev) &&
+ (bmsr & BMSR_LSTATUS)) {
+ u32 val = br32(bp, B44_TX_CTRL);
+ u32 local_adv, remote_adv;
+
+ if (bp->flags & B44_FLAG_FULL_DUPLEX)
+ val |= TX_CTRL_DUPLEX;
+ else
+ val &= ~TX_CTRL_DUPLEX;
+ bw32(bp, B44_TX_CTRL, val);
+
+ if (!(bp->flags & B44_FLAG_FORCE_LINK) &&
+ !b44_readphy(bp, MII_ADVERTISE, &local_adv) &&
+ !b44_readphy(bp, MII_LPA, &remote_adv))
+ b44_set_flow_ctrl(bp, local_adv, remote_adv);
+
+ /* Link now up */
+ netif_carrier_on(bp->dev);
+ b44_link_report(bp);
+ } else if (netif_carrier_ok(bp->dev) && !(bmsr & BMSR_LSTATUS)) {
+ /* Link now down */
+ netif_carrier_off(bp->dev);
+ b44_link_report(bp);
+ }
+
+ if (bmsr & BMSR_RFAULT)
+ netdev_warn(bp->dev, "Remote fault detected in PHY\n");
+ if (bmsr & BMSR_JCD)
+ netdev_warn(bp->dev, "Jabber detected in PHY\n");
+ }
+}
+
+static void b44_timer(unsigned long __opaque)
+{
+ struct b44 *bp = (struct b44 *) __opaque;
+
+ spin_lock_irq(&bp->lock);
+
+ b44_check_phy(bp);
+
+ b44_stats_update(bp);
+
+ spin_unlock_irq(&bp->lock);
+
+ mod_timer(&bp->timer, round_jiffies(jiffies + HZ));
+}
+
+static void b44_tx(struct b44 *bp)
+{
+ u32 cur, cons;
+
+ cur = br32(bp, B44_DMATX_STAT) & DMATX_STAT_CDMASK;
+ cur /= sizeof(struct dma_desc);
+
+ /* XXX needs updating when NETIF_F_SG is supported */
+ for (cons = bp->tx_cons; cons != cur; cons = NEXT_TX(cons)) {
+ struct ring_info *rp = &bp->tx_buffers[cons];
+ struct sk_buff *skb = rp->skb;
+
+ BUG_ON(skb == NULL);
+
+ dma_unmap_single(bp->sdev->dma_dev,
+ rp->mapping,
+ skb->len,
+ DMA_TO_DEVICE);
+ rp->skb = NULL;
+ dev_kfree_skb(skb);
+ }
+
+ bp->tx_cons = cons;
+ if (netif_queue_stopped(bp->dev) &&
+ TX_BUFFS_AVAIL(bp) > B44_TX_WAKEUP_THRESH)
+ netif_wake_queue(bp->dev);
+
+ bw32(bp, B44_GPTIMER, 0);
+}
+
+/* Works like this. This chip writes a 'struct rx_header" 30 bytes
+ * before the DMA address you give it. So we allocate 30 more bytes
+ * for the RX buffer, DMA map all of it, skb_reserve the 30 bytes, then
+ * point the chip at 30 bytes past where the rx_header will go.
+ */
+static int b44_alloc_rx_skb(struct b44 *bp, int src_idx, u32 dest_idx_unmasked)
+{
+ struct dma_desc *dp;
+ struct ring_info *src_map, *map;
+ struct rx_header *rh;
+ struct sk_buff *skb;
+ dma_addr_t mapping;
+ int dest_idx;
+ u32 ctrl;
+
+ src_map = NULL;
+ if (src_idx >= 0)
+ src_map = &bp->rx_buffers[src_idx];
+ dest_idx = dest_idx_unmasked & (B44_RX_RING_SIZE - 1);
+ map = &bp->rx_buffers[dest_idx];
+ skb = netdev_alloc_skb(bp->dev, RX_PKT_BUF_SZ);
+ if (skb == NULL)
+ return -ENOMEM;
+
+ mapping = dma_map_single(bp->sdev->dma_dev, skb->data,
+ RX_PKT_BUF_SZ,
+ DMA_FROM_DEVICE);
+
+ /* Hardware bug work-around, the chip is unable to do PCI DMA
+ to/from anything above 1GB :-( */
+ if (dma_mapping_error(bp->sdev->dma_dev, mapping) ||
+ mapping + RX_PKT_BUF_SZ > DMA_BIT_MASK(30)) {
+ /* Sigh... */
+ if (!dma_mapping_error(bp->sdev->dma_dev, mapping))
+ dma_unmap_single(bp->sdev->dma_dev, mapping,
+ RX_PKT_BUF_SZ, DMA_FROM_DEVICE);
+ dev_kfree_skb_any(skb);
+ skb = __netdev_alloc_skb(bp->dev, RX_PKT_BUF_SZ, GFP_ATOMIC|GFP_DMA);
+ if (skb == NULL)
+ return -ENOMEM;
+ mapping = dma_map_single(bp->sdev->dma_dev, skb->data,
+ RX_PKT_BUF_SZ,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(bp->sdev->dma_dev, mapping) ||
+ mapping + RX_PKT_BUF_SZ > DMA_BIT_MASK(30)) {
+ if (!dma_mapping_error(bp->sdev->dma_dev, mapping))
+ dma_unmap_single(bp->sdev->dma_dev, mapping, RX_PKT_BUF_SZ,DMA_FROM_DEVICE);
+ dev_kfree_skb_any(skb);
+ return -ENOMEM;
+ }
+ bp->force_copybreak = 1;
+ }
+
+ rh = (struct rx_header *) skb->data;
+
+ rh->len = 0;
+ rh->flags = 0;
+
+ map->skb = skb;
+ map->mapping = mapping;
+
+ if (src_map != NULL)
+ src_map->skb = NULL;
+
+ ctrl = (DESC_CTRL_LEN & RX_PKT_BUF_SZ);
+ if (dest_idx == (B44_RX_RING_SIZE - 1))
+ ctrl |= DESC_CTRL_EOT;
+
+ dp = &bp->rx_ring[dest_idx];
+ dp->ctrl = cpu_to_le32(ctrl);
+ dp->addr = cpu_to_le32((u32) mapping + bp->dma_offset);
+
+ if (bp->flags & B44_FLAG_RX_RING_HACK)
+ b44_sync_dma_desc_for_device(bp->sdev, bp->rx_ring_dma,
+ dest_idx * sizeof(*dp),
+ DMA_BIDIRECTIONAL);
+
+ return RX_PKT_BUF_SZ;
+}
+
+static void b44_recycle_rx(struct b44 *bp, int src_idx, u32 dest_idx_unmasked)
+{
+ struct dma_desc *src_desc, *dest_desc;
+ struct ring_info *src_map, *dest_map;
+ struct rx_header *rh;
+ int dest_idx;
+ __le32 ctrl;
+
+ dest_idx = dest_idx_unmasked & (B44_RX_RING_SIZE - 1);
+ dest_desc = &bp->rx_ring[dest_idx];
+ dest_map = &bp->rx_buffers[dest_idx];
+ src_desc = &bp->rx_ring[src_idx];
+ src_map = &bp->rx_buffers[src_idx];
+
+ dest_map->skb = src_map->skb;
+ rh = (struct rx_header *) src_map->skb->data;
+ rh->len = 0;
+ rh->flags = 0;
+ dest_map->mapping = src_map->mapping;
+
+ if (bp->flags & B44_FLAG_RX_RING_HACK)
+ b44_sync_dma_desc_for_cpu(bp->sdev, bp->rx_ring_dma,
+ src_idx * sizeof(*src_desc),
+ DMA_BIDIRECTIONAL);
+
+ ctrl = src_desc->ctrl;
+ if (dest_idx == (B44_RX_RING_SIZE - 1))
+ ctrl |= cpu_to_le32(DESC_CTRL_EOT);
+ else
+ ctrl &= cpu_to_le32(~DESC_CTRL_EOT);
+
+ dest_desc->ctrl = ctrl;
+ dest_desc->addr = src_desc->addr;
+
+ src_map->skb = NULL;
+
+ if (bp->flags & B44_FLAG_RX_RING_HACK)
+ b44_sync_dma_desc_for_device(bp->sdev, bp->rx_ring_dma,
+ dest_idx * sizeof(*dest_desc),
+ DMA_BIDIRECTIONAL);
+
+ dma_sync_single_for_device(bp->sdev->dma_dev, dest_map->mapping,
+ RX_PKT_BUF_SZ,
+ DMA_FROM_DEVICE);
+}
+
+static int b44_rx(struct b44 *bp, int budget)
+{
+ int received;
+ u32 cons, prod;
+
+ received = 0;
+ prod = br32(bp, B44_DMARX_STAT) & DMARX_STAT_CDMASK;
+ prod /= sizeof(struct dma_desc);
+ cons = bp->rx_cons;
+
+ while (cons != prod && budget > 0) {
+ struct ring_info *rp = &bp->rx_buffers[cons];
+ struct sk_buff *skb = rp->skb;
+ dma_addr_t map = rp->mapping;
+ struct rx_header *rh;
+ u16 len;
+
+ dma_sync_single_for_cpu(bp->sdev->dma_dev, map,
+ RX_PKT_BUF_SZ,
+ DMA_FROM_DEVICE);
+ rh = (struct rx_header *) skb->data;
+ len = le16_to_cpu(rh->len);
+ if ((len > (RX_PKT_BUF_SZ - RX_PKT_OFFSET)) ||
+ (rh->flags & cpu_to_le16(RX_FLAG_ERRORS))) {
+ drop_it:
+ b44_recycle_rx(bp, cons, bp->rx_prod);
+ drop_it_no_recycle:
+ bp->dev->stats.rx_dropped++;
+ goto next_pkt;
+ }
+
+ if (len == 0) {
+ int i = 0;
+
+ do {
+ udelay(2);
+ barrier();
+ len = le16_to_cpu(rh->len);
+ } while (len == 0 && i++ < 5);
+ if (len == 0)
+ goto drop_it;
+ }
+
+ /* Omit CRC. */
+ len -= 4;
+
+ if (!bp->force_copybreak && len > RX_COPY_THRESHOLD) {
+ int skb_size;
+ skb_size = b44_alloc_rx_skb(bp, cons, bp->rx_prod);
+ if (skb_size < 0)
+ goto drop_it;
+ dma_unmap_single(bp->sdev->dma_dev, map,
+ skb_size, DMA_FROM_DEVICE);
+ /* Leave out rx_header */
+ skb_put(skb, len + RX_PKT_OFFSET);
+ skb_pull(skb, RX_PKT_OFFSET);
+ } else {
+ struct sk_buff *copy_skb;
+
+ b44_recycle_rx(bp, cons, bp->rx_prod);
+ copy_skb = netdev_alloc_skb(bp->dev, len + 2);
+ if (copy_skb == NULL)
+ goto drop_it_no_recycle;
+
+ skb_reserve(copy_skb, 2);
+ skb_put(copy_skb, len);
+ /* DMA sync done above, copy just the actual packet */
+ skb_copy_from_linear_data_offset(skb, RX_PKT_OFFSET,
+ copy_skb->data, len);
+ skb = copy_skb;
+ }
+ skb_checksum_none_assert(skb);
+ skb->protocol = eth_type_trans(skb, bp->dev);
+ netif_receive_skb(skb);
+ received++;
+ budget--;
+ next_pkt:
+ bp->rx_prod = (bp->rx_prod + 1) &
+ (B44_RX_RING_SIZE - 1);
+ cons = (cons + 1) & (B44_RX_RING_SIZE - 1);
+ }
+
+ bp->rx_cons = cons;
+ bw32(bp, B44_DMARX_PTR, cons * sizeof(struct dma_desc));
+
+ return received;
+}
+
+static int b44_poll(struct napi_struct *napi, int budget)
+{
+ struct b44 *bp = container_of(napi, struct b44, napi);
+ int work_done;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bp->lock, flags);
+
+ if (bp->istat & (ISTAT_TX | ISTAT_TO)) {
+ /* spin_lock(&bp->tx_lock); */
+ b44_tx(bp);
+ /* spin_unlock(&bp->tx_lock); */
+ }
+ if (bp->istat & ISTAT_RFO) { /* fast recovery, in ~20msec */
+ bp->istat &= ~ISTAT_RFO;
+ b44_disable_ints(bp);
+ ssb_device_enable(bp->sdev, 0); /* resets ISTAT_RFO */
+ b44_init_rings(bp);
+ b44_init_hw(bp, B44_FULL_RESET_SKIP_PHY);
+ netif_wake_queue(bp->dev);
+ }
+
+ spin_unlock_irqrestore(&bp->lock, flags);
+
+ work_done = 0;
+ if (bp->istat & ISTAT_RX)
+ work_done += b44_rx(bp, budget);
+
+ if (bp->istat & ISTAT_ERRORS) {
+ spin_lock_irqsave(&bp->lock, flags);
+ b44_halt(bp);
+ b44_init_rings(bp);
+ b44_init_hw(bp, B44_FULL_RESET_SKIP_PHY);
+ netif_wake_queue(bp->dev);
+ spin_unlock_irqrestore(&bp->lock, flags);
+ work_done = 0;
+ }
+
+ if (work_done < budget) {
+ napi_complete(napi);
+ b44_enable_ints(bp);
+ }
+
+ return work_done;
+}
+
+static irqreturn_t b44_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct b44 *bp = netdev_priv(dev);
+ u32 istat, imask;
+ int handled = 0;
+
+ spin_lock(&bp->lock);
+
+ istat = br32(bp, B44_ISTAT);
+ imask = br32(bp, B44_IMASK);
+
+ /* The interrupt mask register controls which interrupt bits
+ * will actually raise an interrupt to the CPU when set by hw/firmware,
+ * but doesn't mask off the bits.
+ */
+ istat &= imask;
+ if (istat) {
+ handled = 1;
+
+ if (unlikely(!netif_running(dev))) {
+ netdev_info(dev, "late interrupt\n");
+ goto irq_ack;
+ }
+
+ if (napi_schedule_prep(&bp->napi)) {
+ /* NOTE: These writes are posted by the readback of
+ * the ISTAT register below.
+ */
+ bp->istat = istat;
+ __b44_disable_ints(bp);
+ __napi_schedule(&bp->napi);
+ }
+
+irq_ack:
+ bw32(bp, B44_ISTAT, istat);
+ br32(bp, B44_ISTAT);
+ }
+ spin_unlock(&bp->lock);
+ return IRQ_RETVAL(handled);
+}
+
+static void b44_tx_timeout(struct net_device *dev)
+{
+ struct b44 *bp = netdev_priv(dev);
+
+ netdev_err(dev, "transmit timed out, resetting\n");
+
+ spin_lock_irq(&bp->lock);
+
+ b44_halt(bp);
+ b44_init_rings(bp);
+ b44_init_hw(bp, B44_FULL_RESET);
+
+ spin_unlock_irq(&bp->lock);
+
+ b44_enable_ints(bp);
+
+ netif_wake_queue(dev);
+}
+
+static netdev_tx_t b44_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct b44 *bp = netdev_priv(dev);
+ int rc = NETDEV_TX_OK;
+ dma_addr_t mapping;
+ u32 len, entry, ctrl;
+ unsigned long flags;
+
+ len = skb->len;
+ spin_lock_irqsave(&bp->lock, flags);
+
+ /* This is a hard error, log it. */
+ if (unlikely(TX_BUFFS_AVAIL(bp) < 1)) {
+ netif_stop_queue(dev);
+ netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
+ goto err_out;
+ }
+
+ mapping = dma_map_single(bp->sdev->dma_dev, skb->data, len, DMA_TO_DEVICE);
+ if (dma_mapping_error(bp->sdev->dma_dev, mapping) || mapping + len > DMA_BIT_MASK(30)) {
+ struct sk_buff *bounce_skb;
+
+ /* Chip can't handle DMA to/from >1GB, use bounce buffer */
+ if (!dma_mapping_error(bp->sdev->dma_dev, mapping))
+ dma_unmap_single(bp->sdev->dma_dev, mapping, len,
+ DMA_TO_DEVICE);
+
+ bounce_skb = __netdev_alloc_skb(dev, len, GFP_ATOMIC | GFP_DMA);
+ if (!bounce_skb)
+ goto err_out;
+
+ mapping = dma_map_single(bp->sdev->dma_dev, bounce_skb->data,
+ len, DMA_TO_DEVICE);
+ if (dma_mapping_error(bp->sdev->dma_dev, mapping) || mapping + len > DMA_BIT_MASK(30)) {
+ if (!dma_mapping_error(bp->sdev->dma_dev, mapping))
+ dma_unmap_single(bp->sdev->dma_dev, mapping,
+ len, DMA_TO_DEVICE);
+ dev_kfree_skb_any(bounce_skb);
+ goto err_out;
+ }
+
+ skb_copy_from_linear_data(skb, skb_put(bounce_skb, len), len);
+ dev_kfree_skb_any(skb);
+ skb = bounce_skb;
+ }
+
+ entry = bp->tx_prod;
+ bp->tx_buffers[entry].skb = skb;
+ bp->tx_buffers[entry].mapping = mapping;
+
+ ctrl = (len & DESC_CTRL_LEN);
+ ctrl |= DESC_CTRL_IOC | DESC_CTRL_SOF | DESC_CTRL_EOF;
+ if (entry == (B44_TX_RING_SIZE - 1))
+ ctrl |= DESC_CTRL_EOT;
+
+ bp->tx_ring[entry].ctrl = cpu_to_le32(ctrl);
+ bp->tx_ring[entry].addr = cpu_to_le32((u32) mapping+bp->dma_offset);
+
+ if (bp->flags & B44_FLAG_TX_RING_HACK)
+ b44_sync_dma_desc_for_device(bp->sdev, bp->tx_ring_dma,
+ entry * sizeof(bp->tx_ring[0]),
+ DMA_TO_DEVICE);
+
+ entry = NEXT_TX(entry);
+
+ bp->tx_prod = entry;
+
+ wmb();
+
+ bw32(bp, B44_DMATX_PTR, entry * sizeof(struct dma_desc));
+ if (bp->flags & B44_FLAG_BUGGY_TXPTR)
+ bw32(bp, B44_DMATX_PTR, entry * sizeof(struct dma_desc));
+ if (bp->flags & B44_FLAG_REORDER_BUG)
+ br32(bp, B44_DMATX_PTR);
+
+ if (TX_BUFFS_AVAIL(bp) < 1)
+ netif_stop_queue(dev);
+
+out_unlock:
+ spin_unlock_irqrestore(&bp->lock, flags);
+
+ return rc;
+
+err_out:
+ rc = NETDEV_TX_BUSY;
+ goto out_unlock;
+}
+
+static int b44_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct b44 *bp = netdev_priv(dev);
+
+ if (new_mtu < B44_MIN_MTU || new_mtu > B44_MAX_MTU)
+ return -EINVAL;
+
+ if (!netif_running(dev)) {
+ /* We'll just catch it later when the
+ * device is up'd.
+ */
+ dev->mtu = new_mtu;
+ return 0;
+ }
+
+ spin_lock_irq(&bp->lock);
+ b44_halt(bp);
+ dev->mtu = new_mtu;
+ b44_init_rings(bp);
+ b44_init_hw(bp, B44_FULL_RESET);
+ spin_unlock_irq(&bp->lock);
+
+ b44_enable_ints(bp);
+
+ return 0;
+}
+
+/* Free up pending packets in all rx/tx rings.
+ *
+ * The chip has been shut down and the driver detached from
+ * the networking, so no interrupts or new tx packets will
+ * end up in the driver. bp->lock is not held and we are not
+ * in an interrupt context and thus may sleep.
+ */
+static void b44_free_rings(struct b44 *bp)
+{
+ struct ring_info *rp;
+ int i;
+
+ for (i = 0; i < B44_RX_RING_SIZE; i++) {
+ rp = &bp->rx_buffers[i];
+
+ if (rp->skb == NULL)
+ continue;
+ dma_unmap_single(bp->sdev->dma_dev, rp->mapping, RX_PKT_BUF_SZ,
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_any(rp->skb);
+ rp->skb = NULL;
+ }
+
+ /* XXX needs changes once NETIF_F_SG is set... */
+ for (i = 0; i < B44_TX_RING_SIZE; i++) {
+ rp = &bp->tx_buffers[i];
+
+ if (rp->skb == NULL)
+ continue;
+ dma_unmap_single(bp->sdev->dma_dev, rp->mapping, rp->skb->len,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_any(rp->skb);
+ rp->skb = NULL;
+ }
+}
+
+/* Initialize tx/rx rings for packet processing.
+ *
+ * The chip has been shut down and the driver detached from
+ * the networking, so no interrupts or new tx packets will
+ * end up in the driver.
+ */
+static void b44_init_rings(struct b44 *bp)
+{
+ int i;
+
+ b44_free_rings(bp);
+
+ memset(bp->rx_ring, 0, B44_RX_RING_BYTES);
+ memset(bp->tx_ring, 0, B44_TX_RING_BYTES);
+
+ if (bp->flags & B44_FLAG_RX_RING_HACK)
+ dma_sync_single_for_device(bp->sdev->dma_dev, bp->rx_ring_dma,
+ DMA_TABLE_BYTES, DMA_BIDIRECTIONAL);
+
+ if (bp->flags & B44_FLAG_TX_RING_HACK)
+ dma_sync_single_for_device(bp->sdev->dma_dev, bp->tx_ring_dma,
+ DMA_TABLE_BYTES, DMA_TO_DEVICE);
+
+ for (i = 0; i < bp->rx_pending; i++) {
+ if (b44_alloc_rx_skb(bp, -1, i) < 0)
+ break;
+ }
+}
+
+/*
+ * Must not be invoked with interrupt sources disabled and
+ * the hardware shutdown down.
+ */
+static void b44_free_consistent(struct b44 *bp)
+{
+ kfree(bp->rx_buffers);
+ bp->rx_buffers = NULL;
+ kfree(bp->tx_buffers);
+ bp->tx_buffers = NULL;
+ if (bp->rx_ring) {
+ if (bp->flags & B44_FLAG_RX_RING_HACK) {
+ dma_unmap_single(bp->sdev->dma_dev, bp->rx_ring_dma,
+ DMA_TABLE_BYTES, DMA_BIDIRECTIONAL);
+ kfree(bp->rx_ring);
+ } else
+ dma_free_coherent(bp->sdev->dma_dev, DMA_TABLE_BYTES,
+ bp->rx_ring, bp->rx_ring_dma);
+ bp->rx_ring = NULL;
+ bp->flags &= ~B44_FLAG_RX_RING_HACK;
+ }
+ if (bp->tx_ring) {
+ if (bp->flags & B44_FLAG_TX_RING_HACK) {
+ dma_unmap_single(bp->sdev->dma_dev, bp->tx_ring_dma,
+ DMA_TABLE_BYTES, DMA_TO_DEVICE);
+ kfree(bp->tx_ring);
+ } else
+ dma_free_coherent(bp->sdev->dma_dev, DMA_TABLE_BYTES,
+ bp->tx_ring, bp->tx_ring_dma);
+ bp->tx_ring = NULL;
+ bp->flags &= ~B44_FLAG_TX_RING_HACK;
+ }
+}
+
+/*
+ * Must not be invoked with interrupt sources disabled and
+ * the hardware shutdown down. Can sleep.
+ */
+static int b44_alloc_consistent(struct b44 *bp, gfp_t gfp)
+{
+ int size;
+
+ size = B44_RX_RING_SIZE * sizeof(struct ring_info);
+ bp->rx_buffers = kzalloc(size, gfp);
+ if (!bp->rx_buffers)
+ goto out_err;
+
+ size = B44_TX_RING_SIZE * sizeof(struct ring_info);
+ bp->tx_buffers = kzalloc(size, gfp);
+ if (!bp->tx_buffers)
+ goto out_err;
+
+ size = DMA_TABLE_BYTES;
+ bp->rx_ring = dma_alloc_coherent(bp->sdev->dma_dev, size,
+ &bp->rx_ring_dma, gfp);
+ if (!bp->rx_ring) {
+ /* Allocation may have failed due to pci_alloc_consistent
+ insisting on use of GFP_DMA, which is more restrictive
+ than necessary... */
+ struct dma_desc *rx_ring;
+ dma_addr_t rx_ring_dma;
+
+ rx_ring = kzalloc(size, gfp);
+ if (!rx_ring)
+ goto out_err;
+
+ rx_ring_dma = dma_map_single(bp->sdev->dma_dev, rx_ring,
+ DMA_TABLE_BYTES,
+ DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(bp->sdev->dma_dev, rx_ring_dma) ||
+ rx_ring_dma + size > DMA_BIT_MASK(30)) {
+ kfree(rx_ring);
+ goto out_err;
+ }
+
+ bp->rx_ring = rx_ring;
+ bp->rx_ring_dma = rx_ring_dma;
+ bp->flags |= B44_FLAG_RX_RING_HACK;
+ }
+
+ bp->tx_ring = dma_alloc_coherent(bp->sdev->dma_dev, size,
+ &bp->tx_ring_dma, gfp);
+ if (!bp->tx_ring) {
+ /* Allocation may have failed due to ssb_dma_alloc_consistent
+ insisting on use of GFP_DMA, which is more restrictive
+ than necessary... */
+ struct dma_desc *tx_ring;
+ dma_addr_t tx_ring_dma;
+
+ tx_ring = kzalloc(size, gfp);
+ if (!tx_ring)
+ goto out_err;
+
+ tx_ring_dma = dma_map_single(bp->sdev->dma_dev, tx_ring,
+ DMA_TABLE_BYTES,
+ DMA_TO_DEVICE);
+
+ if (dma_mapping_error(bp->sdev->dma_dev, tx_ring_dma) ||
+ tx_ring_dma + size > DMA_BIT_MASK(30)) {
+ kfree(tx_ring);
+ goto out_err;
+ }
+
+ bp->tx_ring = tx_ring;
+ bp->tx_ring_dma = tx_ring_dma;
+ bp->flags |= B44_FLAG_TX_RING_HACK;
+ }
+
+ return 0;
+
+out_err:
+ b44_free_consistent(bp);
+ return -ENOMEM;
+}
+
+/* bp->lock is held. */
+static void b44_clear_stats(struct b44 *bp)
+{
+ unsigned long reg;
+
+ bw32(bp, B44_MIB_CTRL, MIB_CTRL_CLR_ON_READ);
+ for (reg = B44_TX_GOOD_O; reg <= B44_TX_PAUSE; reg += 4UL)
+ br32(bp, reg);
+ for (reg = B44_RX_GOOD_O; reg <= B44_RX_NPAUSE; reg += 4UL)
+ br32(bp, reg);
+}
+
+/* bp->lock is held. */
+static void b44_chip_reset(struct b44 *bp, int reset_kind)
+{
+ struct ssb_device *sdev = bp->sdev;
+ bool was_enabled;
+
+ was_enabled = ssb_device_is_enabled(bp->sdev);
+
+ ssb_device_enable(bp->sdev, 0);
+ ssb_pcicore_dev_irqvecs_enable(&sdev->bus->pcicore, sdev);
+
+ if (was_enabled) {
+ bw32(bp, B44_RCV_LAZY, 0);
+ bw32(bp, B44_ENET_CTRL, ENET_CTRL_DISABLE);
+ b44_wait_bit(bp, B44_ENET_CTRL, ENET_CTRL_DISABLE, 200, 1);
+ bw32(bp, B44_DMATX_CTRL, 0);
+ bp->tx_prod = bp->tx_cons = 0;
+ if (br32(bp, B44_DMARX_STAT) & DMARX_STAT_EMASK) {
+ b44_wait_bit(bp, B44_DMARX_STAT, DMARX_STAT_SIDLE,
+ 100, 0);
+ }
+ bw32(bp, B44_DMARX_CTRL, 0);
+ bp->rx_prod = bp->rx_cons = 0;
+ }
+
+ b44_clear_stats(bp);
+
+ /*
+ * Don't enable PHY if we are doing a partial reset
+ * we are probably going to power down
+ */
+ if (reset_kind == B44_CHIP_RESET_PARTIAL)
+ return;
+
+ switch (sdev->bus->bustype) {
+ case SSB_BUSTYPE_SSB:
+ bw32(bp, B44_MDIO_CTRL, (MDIO_CTRL_PREAMBLE |
+ (DIV_ROUND_CLOSEST(ssb_clockspeed(sdev->bus),
+ B44_MDC_RATIO)
+ & MDIO_CTRL_MAXF_MASK)));
+ break;
+ case SSB_BUSTYPE_PCI:
+ bw32(bp, B44_MDIO_CTRL, (MDIO_CTRL_PREAMBLE |
+ (0x0d & MDIO_CTRL_MAXF_MASK)));
+ break;
+ case SSB_BUSTYPE_PCMCIA:
+ case SSB_BUSTYPE_SDIO:
+ WARN_ON(1); /* A device with this bus does not exist. */
+ break;
+ }
+
+ br32(bp, B44_MDIO_CTRL);
+
+ if (!(br32(bp, B44_DEVCTRL) & DEVCTRL_IPP)) {
+ bw32(bp, B44_ENET_CTRL, ENET_CTRL_EPSEL);
+ br32(bp, B44_ENET_CTRL);
+ bp->flags &= ~B44_FLAG_INTERNAL_PHY;
+ } else {
+ u32 val = br32(bp, B44_DEVCTRL);
+
+ if (val & DEVCTRL_EPR) {
+ bw32(bp, B44_DEVCTRL, (val & ~DEVCTRL_EPR));
+ br32(bp, B44_DEVCTRL);
+ udelay(100);
+ }
+ bp->flags |= B44_FLAG_INTERNAL_PHY;
+ }
+}
+
+/* bp->lock is held. */
+static void b44_halt(struct b44 *bp)
+{
+ b44_disable_ints(bp);
+ /* reset PHY */
+ b44_phy_reset(bp);
+ /* power down PHY */
+ netdev_info(bp->dev, "powering down PHY\n");
+ bw32(bp, B44_MAC_CTRL, MAC_CTRL_PHY_PDOWN);
+ /* now reset the chip, but without enabling the MAC&PHY
+ * part of it. This has to be done _after_ we shut down the PHY */
+ b44_chip_reset(bp, B44_CHIP_RESET_PARTIAL);
+}
+
+/* bp->lock is held. */
+static void __b44_set_mac_addr(struct b44 *bp)
+{
+ bw32(bp, B44_CAM_CTRL, 0);
+ if (!(bp->dev->flags & IFF_PROMISC)) {
+ u32 val;
+
+ __b44_cam_write(bp, bp->dev->dev_addr, 0);
+ val = br32(bp, B44_CAM_CTRL);
+ bw32(bp, B44_CAM_CTRL, val | CAM_CTRL_ENABLE);
+ }
+}
+
+static int b44_set_mac_addr(struct net_device *dev, void *p)
+{
+ struct b44 *bp = netdev_priv(dev);
+ struct sockaddr *addr = p;
+ u32 val;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EINVAL;
+
+ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+ spin_lock_irq(&bp->lock);
+
+ val = br32(bp, B44_RXCONFIG);
+ if (!(val & RXCONFIG_CAM_ABSENT))
+ __b44_set_mac_addr(bp);
+
+ spin_unlock_irq(&bp->lock);
+
+ return 0;
+}
+
+/* Called at device open time to get the chip ready for
+ * packet processing. Invoked with bp->lock held.
+ */
+static void __b44_set_rx_mode(struct net_device *);
+static void b44_init_hw(struct b44 *bp, int reset_kind)
+{
+ u32 val;
+
+ b44_chip_reset(bp, B44_CHIP_RESET_FULL);
+ if (reset_kind == B44_FULL_RESET) {
+ b44_phy_reset(bp);
+ b44_setup_phy(bp);
+ }
+
+ /* Enable CRC32, set proper LED modes and power on PHY */
+ bw32(bp, B44_MAC_CTRL, MAC_CTRL_CRC32_ENAB | MAC_CTRL_PHY_LEDCTRL);
+ bw32(bp, B44_RCV_LAZY, (1 << RCV_LAZY_FC_SHIFT));
+
+ /* This sets the MAC address too. */
+ __b44_set_rx_mode(bp->dev);
+
+ /* MTU + eth header + possible VLAN tag + struct rx_header */
+ bw32(bp, B44_RXMAXLEN, bp->dev->mtu + ETH_HLEN + 8 + RX_HEADER_LEN);
+ bw32(bp, B44_TXMAXLEN, bp->dev->mtu + ETH_HLEN + 8 + RX_HEADER_LEN);
+
+ bw32(bp, B44_TX_WMARK, 56); /* XXX magic */
+ if (reset_kind == B44_PARTIAL_RESET) {
+ bw32(bp, B44_DMARX_CTRL, (DMARX_CTRL_ENABLE |
+ (RX_PKT_OFFSET << DMARX_CTRL_ROSHIFT)));
+ } else {
+ bw32(bp, B44_DMATX_CTRL, DMATX_CTRL_ENABLE);
+ bw32(bp, B44_DMATX_ADDR, bp->tx_ring_dma + bp->dma_offset);
+ bw32(bp, B44_DMARX_CTRL, (DMARX_CTRL_ENABLE |
+ (RX_PKT_OFFSET << DMARX_CTRL_ROSHIFT)));
+ bw32(bp, B44_DMARX_ADDR, bp->rx_ring_dma + bp->dma_offset);
+
+ bw32(bp, B44_DMARX_PTR, bp->rx_pending);
+ bp->rx_prod = bp->rx_pending;
+
+ bw32(bp, B44_MIB_CTRL, MIB_CTRL_CLR_ON_READ);
+ }
+
+ val = br32(bp, B44_ENET_CTRL);
+ bw32(bp, B44_ENET_CTRL, (val | ENET_CTRL_ENABLE));
+}
+
+static int b44_open(struct net_device *dev)
+{
+ struct b44 *bp = netdev_priv(dev);
+ int err;
+
+ err = b44_alloc_consistent(bp, GFP_KERNEL);
+ if (err)
+ goto out;
+
+ napi_enable(&bp->napi);
+
+ b44_init_rings(bp);
+ b44_init_hw(bp, B44_FULL_RESET);
+
+ b44_check_phy(bp);
+
+ err = request_irq(dev->irq, b44_interrupt, IRQF_SHARED, dev->name, dev);
+ if (unlikely(err < 0)) {
+ napi_disable(&bp->napi);
+ b44_chip_reset(bp, B44_CHIP_RESET_PARTIAL);
+ b44_free_rings(bp);
+ b44_free_consistent(bp);
+ goto out;
+ }
+
+ init_timer(&bp->timer);
+ bp->timer.expires = jiffies + HZ;
+ bp->timer.data = (unsigned long) bp;
+ bp->timer.function = b44_timer;
+ add_timer(&bp->timer);
+
+ b44_enable_ints(bp);
+ netif_start_queue(dev);
+out:
+ return err;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling receive - used by netconsole and other diagnostic tools
+ * to allow network i/o with interrupts disabled.
+ */
+static void b44_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ b44_interrupt(dev->irq, dev);
+ enable_irq(dev->irq);
+}
+#endif
+
+static void bwfilter_table(struct b44 *bp, u8 *pp, u32 bytes, u32 table_offset)
+{
+ u32 i;
+ u32 *pattern = (u32 *) pp;
+
+ for (i = 0; i < bytes; i += sizeof(u32)) {
+ bw32(bp, B44_FILT_ADDR, table_offset + i);
+ bw32(bp, B44_FILT_DATA, pattern[i / sizeof(u32)]);
+ }
+}
+
+static int b44_magic_pattern(u8 *macaddr, u8 *ppattern, u8 *pmask, int offset)
+{
+ int magicsync = 6;
+ int k, j, len = offset;
+ int ethaddr_bytes = ETH_ALEN;
+
+ memset(ppattern + offset, 0xff, magicsync);
+ for (j = 0; j < magicsync; j++)
+ set_bit(len++, (unsigned long *) pmask);
+
+ for (j = 0; j < B44_MAX_PATTERNS; j++) {
+ if ((B44_PATTERN_SIZE - len) >= ETH_ALEN)
+ ethaddr_bytes = ETH_ALEN;
+ else
+ ethaddr_bytes = B44_PATTERN_SIZE - len;
+ if (ethaddr_bytes <=0)
+ break;
+ for (k = 0; k< ethaddr_bytes; k++) {
+ ppattern[offset + magicsync +
+ (j * ETH_ALEN) + k] = macaddr[k];
+ set_bit(len++, (unsigned long *) pmask);
+ }
+ }
+ return len - 1;
+}
+
+/* Setup magic packet patterns in the b44 WOL
+ * pattern matching filter.
+ */
+static void b44_setup_pseudo_magicp(struct b44 *bp)
+{
+
+ u32 val;
+ int plen0, plen1, plen2;
+ u8 *pwol_pattern;
+ u8 pwol_mask[B44_PMASK_SIZE];
+
+ pwol_pattern = kzalloc(B44_PATTERN_SIZE, GFP_KERNEL);
+ if (!pwol_pattern) {
+ pr_err("Memory not available for WOL\n");
+ return;
+ }
+
+ /* Ipv4 magic packet pattern - pattern 0.*/
+ memset(pwol_mask, 0, B44_PMASK_SIZE);
+ plen0 = b44_magic_pattern(bp->dev->dev_addr, pwol_pattern, pwol_mask,
+ B44_ETHIPV4UDP_HLEN);
+
+ bwfilter_table(bp, pwol_pattern, B44_PATTERN_SIZE, B44_PATTERN_BASE);
+ bwfilter_table(bp, pwol_mask, B44_PMASK_SIZE, B44_PMASK_BASE);
+
+ /* Raw ethernet II magic packet pattern - pattern 1 */
+ memset(pwol_pattern, 0, B44_PATTERN_SIZE);
+ memset(pwol_mask, 0, B44_PMASK_SIZE);
+ plen1 = b44_magic_pattern(bp->dev->dev_addr, pwol_pattern, pwol_mask,
+ ETH_HLEN);
+
+ bwfilter_table(bp, pwol_pattern, B44_PATTERN_SIZE,
+ B44_PATTERN_BASE + B44_PATTERN_SIZE);
+ bwfilter_table(bp, pwol_mask, B44_PMASK_SIZE,
+ B44_PMASK_BASE + B44_PMASK_SIZE);
+
+ /* Ipv6 magic packet pattern - pattern 2 */
+ memset(pwol_pattern, 0, B44_PATTERN_SIZE);
+ memset(pwol_mask, 0, B44_PMASK_SIZE);
+ plen2 = b44_magic_pattern(bp->dev->dev_addr, pwol_pattern, pwol_mask,
+ B44_ETHIPV6UDP_HLEN);
+
+ bwfilter_table(bp, pwol_pattern, B44_PATTERN_SIZE,
+ B44_PATTERN_BASE + B44_PATTERN_SIZE + B44_PATTERN_SIZE);
+ bwfilter_table(bp, pwol_mask, B44_PMASK_SIZE,
+ B44_PMASK_BASE + B44_PMASK_SIZE + B44_PMASK_SIZE);
+
+ kfree(pwol_pattern);
+
+ /* set these pattern's lengths: one less than each real length */
+ val = plen0 | (plen1 << 8) | (plen2 << 16) | WKUP_LEN_ENABLE_THREE;
+ bw32(bp, B44_WKUP_LEN, val);
+
+ /* enable wakeup pattern matching */
+ val = br32(bp, B44_DEVCTRL);
+ bw32(bp, B44_DEVCTRL, val | DEVCTRL_PFE);
+
+}
+
+#ifdef CONFIG_B44_PCI
+static void b44_setup_wol_pci(struct b44 *bp)
+{
+ u16 val;
+
+ if (bp->sdev->bus->bustype != SSB_BUSTYPE_SSB) {
+ bw32(bp, SSB_TMSLOW, br32(bp, SSB_TMSLOW) | SSB_TMSLOW_PE);
+ pci_read_config_word(bp->sdev->bus->host_pci, SSB_PMCSR, &val);
+ pci_write_config_word(bp->sdev->bus->host_pci, SSB_PMCSR, val | SSB_PE);
+ }
+}
+#else
+static inline void b44_setup_wol_pci(struct b44 *bp) { }
+#endif /* CONFIG_B44_PCI */
+
+static void b44_setup_wol(struct b44 *bp)
+{
+ u32 val;
+
+ bw32(bp, B44_RXCONFIG, RXCONFIG_ALLMULTI);
+
+ if (bp->flags & B44_FLAG_B0_ANDLATER) {
+
+ bw32(bp, B44_WKUP_LEN, WKUP_LEN_DISABLE);
+
+ val = bp->dev->dev_addr[2] << 24 |
+ bp->dev->dev_addr[3] << 16 |
+ bp->dev->dev_addr[4] << 8 |
+ bp->dev->dev_addr[5];
+ bw32(bp, B44_ADDR_LO, val);
+
+ val = bp->dev->dev_addr[0] << 8 |
+ bp->dev->dev_addr[1];
+ bw32(bp, B44_ADDR_HI, val);
+
+ val = br32(bp, B44_DEVCTRL);
+ bw32(bp, B44_DEVCTRL, val | DEVCTRL_MPM | DEVCTRL_PFE);
+
+ } else {
+ b44_setup_pseudo_magicp(bp);
+ }
+ b44_setup_wol_pci(bp);
+}
+
+static int b44_close(struct net_device *dev)
+{
+ struct b44 *bp = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+
+ napi_disable(&bp->napi);
+
+ del_timer_sync(&bp->timer);
+
+ spin_lock_irq(&bp->lock);
+
+ b44_halt(bp);
+ b44_free_rings(bp);
+ netif_carrier_off(dev);
+
+ spin_unlock_irq(&bp->lock);
+
+ free_irq(dev->irq, dev);
+
+ if (bp->flags & B44_FLAG_WOL_ENABLE) {
+ b44_init_hw(bp, B44_PARTIAL_RESET);
+ b44_setup_wol(bp);
+ }
+
+ b44_free_consistent(bp);
+
+ return 0;
+}
+
+static struct net_device_stats *b44_get_stats(struct net_device *dev)
+{
+ struct b44 *bp = netdev_priv(dev);
+ struct net_device_stats *nstat = &dev->stats;
+ struct b44_hw_stats *hwstat = &bp->hw_stats;
+
+ /* Convert HW stats into netdevice stats. */
+ nstat->rx_packets = hwstat->rx_pkts;
+ nstat->tx_packets = hwstat->tx_pkts;
+ nstat->rx_bytes = hwstat->rx_octets;
+ nstat->tx_bytes = hwstat->tx_octets;
+ nstat->tx_errors = (hwstat->tx_jabber_pkts +
+ hwstat->tx_oversize_pkts +
+ hwstat->tx_underruns +
+ hwstat->tx_excessive_cols +
+ hwstat->tx_late_cols);
+ nstat->multicast = hwstat->tx_multicast_pkts;
+ nstat->collisions = hwstat->tx_total_cols;
+
+ nstat->rx_length_errors = (hwstat->rx_oversize_pkts +
+ hwstat->rx_undersize);
+ nstat->rx_over_errors = hwstat->rx_missed_pkts;
+ nstat->rx_frame_errors = hwstat->rx_align_errs;
+ nstat->rx_crc_errors = hwstat->rx_crc_errs;
+ nstat->rx_errors = (hwstat->rx_jabber_pkts +
+ hwstat->rx_oversize_pkts +
+ hwstat->rx_missed_pkts +
+ hwstat->rx_crc_align_errs +
+ hwstat->rx_undersize +
+ hwstat->rx_crc_errs +
+ hwstat->rx_align_errs +
+ hwstat->rx_symbol_errs);
+
+ nstat->tx_aborted_errors = hwstat->tx_underruns;
+#if 0
+ /* Carrier lost counter seems to be broken for some devices */
+ nstat->tx_carrier_errors = hwstat->tx_carrier_lost;
+#endif
+
+ return nstat;
+}
+
+static int __b44_load_mcast(struct b44 *bp, struct net_device *dev)
+{
+ struct netdev_hw_addr *ha;
+ int i, num_ents;
+
+ num_ents = min_t(int, netdev_mc_count(dev), B44_MCAST_TABLE_SIZE);
+ i = 0;
+ netdev_for_each_mc_addr(ha, dev) {
+ if (i == num_ents)
+ break;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ __b44_cam_write(bp, ha->addr, i++ + 1);
+#else
+ __b44_cam_write(bp, ha->dmi_addr, i++ + 1);
+#endif
+ }
+ return i+1;
+}
+
+static void __b44_set_rx_mode(struct net_device *dev)
+{
+ struct b44 *bp = netdev_priv(dev);
+ u32 val;
+
+ val = br32(bp, B44_RXCONFIG);
+ val &= ~(RXCONFIG_PROMISC | RXCONFIG_ALLMULTI);
+ if ((dev->flags & IFF_PROMISC) || (val & RXCONFIG_CAM_ABSENT)) {
+ val |= RXCONFIG_PROMISC;
+ bw32(bp, B44_RXCONFIG, val);
+ } else {
+ unsigned char zero[6] = {0, 0, 0, 0, 0, 0};
+ int i = 1;
+
+ __b44_set_mac_addr(bp);
+
+ if ((dev->flags & IFF_ALLMULTI) ||
+ (netdev_mc_count(dev) > B44_MCAST_TABLE_SIZE))
+ val |= RXCONFIG_ALLMULTI;
+ else
+ i = __b44_load_mcast(bp, dev);
+
+ for (; i < 64; i++)
+ __b44_cam_write(bp, zero, i);
+
+ bw32(bp, B44_RXCONFIG, val);
+ val = br32(bp, B44_CAM_CTRL);
+ bw32(bp, B44_CAM_CTRL, val | CAM_CTRL_ENABLE);
+ }
+}
+
+static void b44_set_rx_mode(struct net_device *dev)
+{
+ struct b44 *bp = netdev_priv(dev);
+
+ spin_lock_irq(&bp->lock);
+ __b44_set_rx_mode(dev);
+ spin_unlock_irq(&bp->lock);
+}
+
+static u32 b44_get_msglevel(struct net_device *dev)
+{
+ struct b44 *bp = netdev_priv(dev);
+ return bp->msg_enable;
+}
+
+static void b44_set_msglevel(struct net_device *dev, u32 value)
+{
+ struct b44 *bp = netdev_priv(dev);
+ bp->msg_enable = value;
+}
+
+static void b44_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct b44 *bp = netdev_priv(dev);
+ struct ssb_bus *bus = bp->sdev->bus;
+
+ strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version));
+ switch (bus->bustype) {
+ case SSB_BUSTYPE_PCI:
+ strlcpy(info->bus_info, pci_name(bus->host_pci), sizeof(info->bus_info));
+ break;
+ case SSB_BUSTYPE_SSB:
+ strlcpy(info->bus_info, "SSB", sizeof(info->bus_info));
+ break;
+ case SSB_BUSTYPE_PCMCIA:
+ case SSB_BUSTYPE_SDIO:
+ WARN_ON(1); /* A device with this bus does not exist. */
+ break;
+ }
+}
+
+static int b44_nway_reset(struct net_device *dev)
+{
+ struct b44 *bp = netdev_priv(dev);
+ u32 bmcr;
+ int r;
+
+ spin_lock_irq(&bp->lock);
+ b44_readphy(bp, MII_BMCR, &bmcr);
+ b44_readphy(bp, MII_BMCR, &bmcr);
+ r = -EINVAL;
+ if (bmcr & BMCR_ANENABLE) {
+ b44_writephy(bp, MII_BMCR,
+ bmcr | BMCR_ANRESTART);
+ r = 0;
+ }
+ spin_unlock_irq(&bp->lock);
+
+ return r;
+}
+
+static int b44_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct b44 *bp = netdev_priv(dev);
+
+ cmd->supported = (SUPPORTED_Autoneg);
+ cmd->supported |= (SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_MII);
+
+ cmd->advertising = 0;
+ if (bp->flags & B44_FLAG_ADV_10HALF)
+ cmd->advertising |= ADVERTISED_10baseT_Half;
+ if (bp->flags & B44_FLAG_ADV_10FULL)
+ cmd->advertising |= ADVERTISED_10baseT_Full;
+ if (bp->flags & B44_FLAG_ADV_100HALF)
+ cmd->advertising |= ADVERTISED_100baseT_Half;
+ if (bp->flags & B44_FLAG_ADV_100FULL)
+ cmd->advertising |= ADVERTISED_100baseT_Full;
+ cmd->advertising |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
+ ethtool_cmd_speed_set(cmd, ((bp->flags & B44_FLAG_100_BASE_T) ?
+ SPEED_100 : SPEED_10));
+ cmd->duplex = (bp->flags & B44_FLAG_FULL_DUPLEX) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ cmd->port = 0;
+ cmd->phy_address = bp->phy_addr;
+ cmd->transceiver = (bp->flags & B44_FLAG_INTERNAL_PHY) ?
+ XCVR_INTERNAL : XCVR_EXTERNAL;
+ cmd->autoneg = (bp->flags & B44_FLAG_FORCE_LINK) ?
+ AUTONEG_DISABLE : AUTONEG_ENABLE;
+ if (cmd->autoneg == AUTONEG_ENABLE)
+ cmd->advertising |= ADVERTISED_Autoneg;
+ if (!netif_running(dev)){
+ ethtool_cmd_speed_set(cmd, 0);
+ cmd->duplex = 0xff;
+ }
+ cmd->maxtxpkt = 0;
+ cmd->maxrxpkt = 0;
+ return 0;
+}
+
+static int b44_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct b44 *bp = netdev_priv(dev);
+ u32 speed = ethtool_cmd_speed(cmd);
+
+ /* We do not support gigabit. */
+ if (cmd->autoneg == AUTONEG_ENABLE) {
+ if (cmd->advertising &
+ (ADVERTISED_1000baseT_Half |
+ ADVERTISED_1000baseT_Full))
+ return -EINVAL;
+ } else if ((speed != SPEED_100 &&
+ speed != SPEED_10) ||
+ (cmd->duplex != DUPLEX_HALF &&
+ cmd->duplex != DUPLEX_FULL)) {
+ return -EINVAL;
+ }
+
+ spin_lock_irq(&bp->lock);
+
+ if (cmd->autoneg == AUTONEG_ENABLE) {
+ bp->flags &= ~(B44_FLAG_FORCE_LINK |
+ B44_FLAG_100_BASE_T |
+ B44_FLAG_FULL_DUPLEX |
+ B44_FLAG_ADV_10HALF |
+ B44_FLAG_ADV_10FULL |
+ B44_FLAG_ADV_100HALF |
+ B44_FLAG_ADV_100FULL);
+ if (cmd->advertising == 0) {
+ bp->flags |= (B44_FLAG_ADV_10HALF |
+ B44_FLAG_ADV_10FULL |
+ B44_FLAG_ADV_100HALF |
+ B44_FLAG_ADV_100FULL);
+ } else {
+ if (cmd->advertising & ADVERTISED_10baseT_Half)
+ bp->flags |= B44_FLAG_ADV_10HALF;
+ if (cmd->advertising & ADVERTISED_10baseT_Full)
+ bp->flags |= B44_FLAG_ADV_10FULL;
+ if (cmd->advertising & ADVERTISED_100baseT_Half)
+ bp->flags |= B44_FLAG_ADV_100HALF;
+ if (cmd->advertising & ADVERTISED_100baseT_Full)
+ bp->flags |= B44_FLAG_ADV_100FULL;
+ }
+ } else {
+ bp->flags |= B44_FLAG_FORCE_LINK;
+ bp->flags &= ~(B44_FLAG_100_BASE_T | B44_FLAG_FULL_DUPLEX);
+ if (speed == SPEED_100)
+ bp->flags |= B44_FLAG_100_BASE_T;
+ if (cmd->duplex == DUPLEX_FULL)
+ bp->flags |= B44_FLAG_FULL_DUPLEX;
+ }
+
+ if (netif_running(dev))
+ b44_setup_phy(bp);
+
+ spin_unlock_irq(&bp->lock);
+
+ return 0;
+}
+
+static void b44_get_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ering)
+{
+ struct b44 *bp = netdev_priv(dev);
+
+ ering->rx_max_pending = B44_RX_RING_SIZE - 1;
+ ering->rx_pending = bp->rx_pending;
+
+ /* XXX ethtool lacks a tx_max_pending, oops... */
+}
+
+static int b44_set_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ering)
+{
+ struct b44 *bp = netdev_priv(dev);
+
+ if ((ering->rx_pending > B44_RX_RING_SIZE - 1) ||
+ (ering->rx_mini_pending != 0) ||
+ (ering->rx_jumbo_pending != 0) ||
+ (ering->tx_pending > B44_TX_RING_SIZE - 1))
+ return -EINVAL;
+
+ spin_lock_irq(&bp->lock);
+
+ bp->rx_pending = ering->rx_pending;
+ bp->tx_pending = ering->tx_pending;
+
+ b44_halt(bp);
+ b44_init_rings(bp);
+ b44_init_hw(bp, B44_FULL_RESET);
+ netif_wake_queue(bp->dev);
+ spin_unlock_irq(&bp->lock);
+
+ b44_enable_ints(bp);
+
+ return 0;
+}
+
+static void b44_get_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *epause)
+{
+ struct b44 *bp = netdev_priv(dev);
+
+ epause->autoneg =
+ (bp->flags & B44_FLAG_PAUSE_AUTO) != 0;
+ epause->rx_pause =
+ (bp->flags & B44_FLAG_RX_PAUSE) != 0;
+ epause->tx_pause =
+ (bp->flags & B44_FLAG_TX_PAUSE) != 0;
+}
+
+static int b44_set_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *epause)
+{
+ struct b44 *bp = netdev_priv(dev);
+
+ spin_lock_irq(&bp->lock);
+ if (epause->autoneg)
+ bp->flags |= B44_FLAG_PAUSE_AUTO;
+ else
+ bp->flags &= ~B44_FLAG_PAUSE_AUTO;
+ if (epause->rx_pause)
+ bp->flags |= B44_FLAG_RX_PAUSE;
+ else
+ bp->flags &= ~B44_FLAG_RX_PAUSE;
+ if (epause->tx_pause)
+ bp->flags |= B44_FLAG_TX_PAUSE;
+ else
+ bp->flags &= ~B44_FLAG_TX_PAUSE;
+ if (bp->flags & B44_FLAG_PAUSE_AUTO) {
+ b44_halt(bp);
+ b44_init_rings(bp);
+ b44_init_hw(bp, B44_FULL_RESET);
+ } else {
+ __b44_set_flow_ctrl(bp, bp->flags);
+ }
+ spin_unlock_irq(&bp->lock);
+
+ b44_enable_ints(bp);
+
+ return 0;
+}
+
+static void b44_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ switch(stringset) {
+ case ETH_SS_STATS:
+ memcpy(data, *b44_gstrings, sizeof(b44_gstrings));
+ break;
+ }
+}
+
+static int b44_get_sset_count(struct net_device *dev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return ARRAY_SIZE(b44_gstrings);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void b44_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct b44 *bp = netdev_priv(dev);
+ u32 *val = &bp->hw_stats.tx_good_octets;
+ u32 i;
+
+ spin_lock_irq(&bp->lock);
+
+ b44_stats_update(bp);
+
+ for (i = 0; i < ARRAY_SIZE(b44_gstrings); i++)
+ *data++ = *val++;
+
+ spin_unlock_irq(&bp->lock);
+}
+
+static void b44_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct b44 *bp = netdev_priv(dev);
+
+ wol->supported = WAKE_MAGIC;
+ if (bp->flags & B44_FLAG_WOL_ENABLE)
+ wol->wolopts = WAKE_MAGIC;
+ else
+ wol->wolopts = 0;
+ memset(&wol->sopass, 0, sizeof(wol->sopass));
+}
+
+static int b44_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct b44 *bp = netdev_priv(dev);
+
+ spin_lock_irq(&bp->lock);
+ if (wol->wolopts & WAKE_MAGIC)
+ bp->flags |= B44_FLAG_WOL_ENABLE;
+ else
+ bp->flags &= ~B44_FLAG_WOL_ENABLE;
+ spin_unlock_irq(&bp->lock);
+
+ return 0;
+}
+
+static const struct ethtool_ops b44_ethtool_ops = {
+ .get_drvinfo = b44_get_drvinfo,
+ .get_settings = b44_get_settings,
+ .set_settings = b44_set_settings,
+ .nway_reset = b44_nway_reset,
+ .get_link = ethtool_op_get_link,
+ .get_wol = b44_get_wol,
+ .set_wol = b44_set_wol,
+ .get_ringparam = b44_get_ringparam,
+ .set_ringparam = b44_set_ringparam,
+ .get_pauseparam = b44_get_pauseparam,
+ .set_pauseparam = b44_set_pauseparam,
+ .get_msglevel = b44_get_msglevel,
+ .set_msglevel = b44_set_msglevel,
+ .get_strings = b44_get_strings,
+ .get_sset_count = b44_get_sset_count,
+ .get_ethtool_stats = b44_get_ethtool_stats,
+};
+
+static int b44_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct mii_ioctl_data *data = if_mii(ifr);
+ struct b44 *bp = netdev_priv(dev);
+ int err = -EINVAL;
+
+ if (!netif_running(dev))
+ goto out;
+
+ spin_lock_irq(&bp->lock);
+ err = generic_mii_ioctl(&bp->mii_if, data, cmd, NULL);
+ spin_unlock_irq(&bp->lock);
+out:
+ return err;
+}
+
+static int __devinit b44_get_invariants(struct b44 *bp)
+{
+ struct ssb_device *sdev = bp->sdev;
+ int err = 0;
+ u8 *addr;
+
+ bp->dma_offset = ssb_dma_translation(sdev);
+
+ if (sdev->bus->bustype == SSB_BUSTYPE_SSB &&
+ instance > 1) {
+ addr = sdev->bus->sprom.et1mac;
+ bp->phy_addr = sdev->bus->sprom.et1phyaddr;
+ } else {
+ addr = sdev->bus->sprom.et0mac;
+ bp->phy_addr = sdev->bus->sprom.et0phyaddr;
+ }
+ /* Some ROMs have buggy PHY addresses with the high
+ * bits set (sign extension?). Truncate them to a
+ * valid PHY address. */
+ bp->phy_addr &= 0x1F;
+
+ memcpy(bp->dev->dev_addr, addr, 6);
+
+ if (!is_valid_ether_addr(&bp->dev->dev_addr[0])){
+ pr_err("Invalid MAC address found in EEPROM\n");
+ return -EINVAL;
+ }
+
+ memcpy(bp->dev->perm_addr, bp->dev->dev_addr, bp->dev->addr_len);
+
+ bp->imask = IMASK_DEF;
+
+ /* XXX - really required?
+ bp->flags |= B44_FLAG_BUGGY_TXPTR;
+ */
+
+ if (bp->sdev->id.revision >= 7)
+ bp->flags |= B44_FLAG_B0_ANDLATER;
+
+ return err;
+}
+
+static const struct net_device_ops b44_netdev_ops = {
+ .ndo_open = b44_open,
+ .ndo_stop = b44_close,
+ .ndo_start_xmit = b44_start_xmit,
+ .ndo_get_stats = b44_get_stats,
+ .ndo_set_rx_mode = b44_set_rx_mode,
+ .ndo_set_mac_address = b44_set_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = b44_ioctl,
+ .ndo_tx_timeout = b44_tx_timeout,
+ .ndo_change_mtu = b44_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = b44_poll_controller,
+#endif
+};
+
+static int __devinit b44_init_one(struct ssb_device *sdev,
+ const struct ssb_device_id *ent)
+{
+ struct net_device *dev;
+ struct b44 *bp;
+ int err;
+
+ instance++;
+
+ pr_info_once("%s version %s\n", DRV_DESCRIPTION, DRV_MODULE_VERSION);
+
+ dev = alloc_etherdev(sizeof(*bp));
+ if (!dev) {
+ dev_err(sdev->dev, "Etherdev alloc failed, aborting\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ SET_NETDEV_DEV(dev, sdev->dev);
+
+ /* No interesting netdevice features in this card... */
+ dev->features |= 0;
+
+ bp = netdev_priv(dev);
+ bp->sdev = sdev;
+ bp->dev = dev;
+ bp->force_copybreak = 0;
+
+ bp->msg_enable = netif_msg_init(b44_debug, B44_DEF_MSG_ENABLE);
+
+ spin_lock_init(&bp->lock);
+
+ bp->rx_pending = B44_DEF_RX_RING_PENDING;
+ bp->tx_pending = B44_DEF_TX_RING_PENDING;
+
+ netdev_attach_ops(dev, &b44_netdev_ops);
+ netif_napi_add(dev, &bp->napi, b44_poll, 64);
+ dev->watchdog_timeo = B44_TX_TIMEOUT;
+ dev->irq = sdev->irq;
+ SET_ETHTOOL_OPS(dev, &b44_ethtool_ops);
+
+ err = ssb_bus_powerup(sdev->bus, 0);
+ if (err) {
+ dev_err(sdev->dev,
+ "Failed to powerup the bus\n");
+ goto err_out_free_dev;
+ }
+
+ if (dma_set_mask(sdev->dma_dev, DMA_BIT_MASK(30)) ||
+ dma_set_coherent_mask(sdev->dma_dev, DMA_BIT_MASK(30))) {
+ dev_err(sdev->dev,
+ "Required 30BIT DMA mask unsupported by the system\n");
+ goto err_out_powerdown;
+ }
+
+ err = b44_get_invariants(bp);
+ if (err) {
+ dev_err(sdev->dev,
+ "Problem fetching invariants of chip, aborting\n");
+ goto err_out_powerdown;
+ }
+
+ bp->mii_if.dev = dev;
+ bp->mii_if.mdio_read = b44_mii_read;
+ bp->mii_if.mdio_write = b44_mii_write;
+ bp->mii_if.phy_id = bp->phy_addr;
+ bp->mii_if.phy_id_mask = 0x1f;
+ bp->mii_if.reg_num_mask = 0x1f;
+
+ /* By default, advertise all speed/duplex settings. */
+ bp->flags |= (B44_FLAG_ADV_10HALF | B44_FLAG_ADV_10FULL |
+ B44_FLAG_ADV_100HALF | B44_FLAG_ADV_100FULL);
+
+ /* By default, auto-negotiate PAUSE. */
+ bp->flags |= B44_FLAG_PAUSE_AUTO;
+
+ err = register_netdev(dev);
+ if (err) {
+ dev_err(sdev->dev, "Cannot register net device, aborting\n");
+ goto err_out_powerdown;
+ }
+
+ netif_carrier_off(dev);
+
+ ssb_set_drvdata(sdev, dev);
+
+ /* Chip reset provides power to the b44 MAC & PCI cores, which
+ * is necessary for MAC register access.
+ */
+ b44_chip_reset(bp, B44_CHIP_RESET_FULL);
+
+ /* do a phy reset to test if there is an active phy */
+ if (b44_phy_reset(bp) < 0)
+ bp->phy_addr = B44_PHY_ADDR_NO_PHY;
+
+ netdev_info(dev, "%s %pM\n", DRV_DESCRIPTION, dev->dev_addr);
+
+ return 0;
+
+err_out_powerdown:
+ ssb_bus_may_powerdown(sdev->bus);
+
+err_out_free_dev:
+ free_netdev(dev);
+
+out:
+ return err;
+}
+
+static void __devexit b44_remove_one(struct ssb_device *sdev)
+{
+ struct net_device *dev = ssb_get_drvdata(sdev);
+
+ unregister_netdev(dev);
+ ssb_device_disable(sdev, 0);
+ ssb_bus_may_powerdown(sdev->bus);
+ free_netdev(dev);
+ ssb_pcihost_set_power_state(sdev, PCI_D3hot);
+ ssb_set_drvdata(sdev, NULL);
+}
+
+static int b44_suspend(struct ssb_device *sdev, pm_message_t state)
+{
+ struct net_device *dev = ssb_get_drvdata(sdev);
+ struct b44 *bp = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return 0;
+
+ del_timer_sync(&bp->timer);
+
+ spin_lock_irq(&bp->lock);
+
+ b44_halt(bp);
+ netif_carrier_off(bp->dev);
+ netif_device_detach(bp->dev);
+ b44_free_rings(bp);
+
+ spin_unlock_irq(&bp->lock);
+
+ free_irq(dev->irq, dev);
+ if (bp->flags & B44_FLAG_WOL_ENABLE) {
+ b44_init_hw(bp, B44_PARTIAL_RESET);
+ b44_setup_wol(bp);
+ }
+
+ ssb_pcihost_set_power_state(sdev, PCI_D3hot);
+ return 0;
+}
+
+static int b44_resume(struct ssb_device *sdev)
+{
+ struct net_device *dev = ssb_get_drvdata(sdev);
+ struct b44 *bp = netdev_priv(dev);
+ int rc = 0;
+
+ rc = ssb_bus_powerup(sdev->bus, 0);
+ if (rc) {
+ dev_err(sdev->dev,
+ "Failed to powerup the bus\n");
+ return rc;
+ }
+
+ if (!netif_running(dev))
+ return 0;
+
+ spin_lock_irq(&bp->lock);
+ b44_init_rings(bp);
+ b44_init_hw(bp, B44_FULL_RESET);
+ spin_unlock_irq(&bp->lock);
+
+ /*
+ * As a shared interrupt, the handler can be called immediately. To be
+ * able to check the interrupt status the hardware must already be
+ * powered back on (b44_init_hw).
+ */
+ rc = request_irq(dev->irq, b44_interrupt, IRQF_SHARED, dev->name, dev);
+ if (rc) {
+ netdev_err(dev, "request_irq failed\n");
+ spin_lock_irq(&bp->lock);
+ b44_halt(bp);
+ b44_free_rings(bp);
+ spin_unlock_irq(&bp->lock);
+ return rc;
+ }
+
+ netif_device_attach(bp->dev);
+
+ b44_enable_ints(bp);
+ netif_wake_queue(dev);
+
+ mod_timer(&bp->timer, jiffies + 1);
+
+ return 0;
+}
+
+static struct ssb_driver b44_ssb_driver = {
+ .name = DRV_MODULE_NAME,
+ .id_table = b44_ssb_tbl,
+ .probe = b44_init_one,
+ .remove = __devexit_p(b44_remove_one),
+ .suspend = b44_suspend,
+ .resume = b44_resume,
+};
+
+static inline int __init b44_pci_init(void)
+{
+ int err = 0;
+#ifdef CONFIG_B44_PCI
+ err = ssb_pcihost_register(&b44_pci_driver);
+#endif
+ return err;
+}
+
+static inline void __exit b44_pci_exit(void)
+{
+#ifdef CONFIG_B44_PCI
+ ssb_pcihost_unregister(&b44_pci_driver);
+#endif
+}
+
+static int __init b44_init(void)
+{
+ unsigned int dma_desc_align_size = dma_get_cache_alignment();
+ int err;
+
+ /* Setup paramaters for syncing RX/TX DMA descriptors */
+ dma_desc_sync_size = max_t(unsigned int, dma_desc_align_size, sizeof(struct dma_desc));
+
+ err = b44_pci_init();
+ if (err)
+ return err;
+ err = ssb_driver_register(&b44_ssb_driver);
+ if (err)
+ b44_pci_exit();
+ return err;
+}
+
+static void __exit b44_cleanup(void)
+{
+ ssb_driver_unregister(&b44_ssb_driver);
+ b44_pci_exit();
+}
+
+module_init(b44_init);
+module_exit(b44_cleanup);
+
diff --git a/drivers/net/ethernet/broadcom/b44.h b/drivers/net/ethernet/broadcom/b44.h
new file mode 100644
index 0000000..e1905a4
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/b44.h
@@ -0,0 +1,401 @@
+#ifndef _B44_H
+#define _B44_H
+
+/* Register layout. (These correspond to struct _bcmenettregs in bcm4400.) */
+#define B44_DEVCTRL 0x0000UL /* Device Control */
+#define DEVCTRL_MPM 0x00000040 /* Magic Packet PME Enable (B0 only) */
+#define DEVCTRL_PFE 0x00000080 /* Pattern Filtering Enable */
+#define DEVCTRL_IPP 0x00000400 /* Internal EPHY Present */
+#define DEVCTRL_EPR 0x00008000 /* EPHY Reset */
+#define DEVCTRL_PME 0x00001000 /* PHY Mode Enable */
+#define DEVCTRL_PMCE 0x00002000 /* PHY Mode Clocks Enable */
+#define DEVCTRL_PADDR 0x0007c000 /* PHY Address */
+#define DEVCTRL_PADDR_SHIFT 18
+#define B44_BIST_STAT 0x000CUL /* Built-In Self-Test Status */
+#define B44_WKUP_LEN 0x0010UL /* Wakeup Length */
+#define WKUP_LEN_P0_MASK 0x0000007f /* Pattern 0 */
+#define WKUP_LEN_D0 0x00000080
+#define WKUP_LEN_P1_MASK 0x00007f00 /* Pattern 1 */
+#define WKUP_LEN_P1_SHIFT 8
+#define WKUP_LEN_D1 0x00008000
+#define WKUP_LEN_P2_MASK 0x007f0000 /* Pattern 2 */
+#define WKUP_LEN_P2_SHIFT 16
+#define WKUP_LEN_D2 0x00000000
+#define WKUP_LEN_P3_MASK 0x7f000000 /* Pattern 3 */
+#define WKUP_LEN_P3_SHIFT 24
+#define WKUP_LEN_D3 0x80000000
+#define WKUP_LEN_DISABLE 0x80808080
+#define WKUP_LEN_ENABLE_TWO 0x80800000
+#define WKUP_LEN_ENABLE_THREE 0x80000000
+#define B44_ISTAT 0x0020UL /* Interrupt Status */
+#define ISTAT_LS 0x00000020 /* Link Change (B0 only) */
+#define ISTAT_PME 0x00000040 /* Power Management Event */
+#define ISTAT_TO 0x00000080 /* General Purpose Timeout */
+#define ISTAT_DSCE 0x00000400 /* Descriptor Error */
+#define ISTAT_DATAE 0x00000800 /* Data Error */
+#define ISTAT_DPE 0x00001000 /* Descr. Protocol Error */
+#define ISTAT_RDU 0x00002000 /* Receive Descr. Underflow */
+#define ISTAT_RFO 0x00004000 /* Receive FIFO Overflow */
+#define ISTAT_TFU 0x00008000 /* Transmit FIFO Underflow */
+#define ISTAT_RX 0x00010000 /* RX Interrupt */
+#define ISTAT_TX 0x01000000 /* TX Interrupt */
+#define ISTAT_EMAC 0x04000000 /* EMAC Interrupt */
+#define ISTAT_MII_WRITE 0x08000000 /* MII Write Interrupt */
+#define ISTAT_MII_READ 0x10000000 /* MII Read Interrupt */
+#define ISTAT_ERRORS (ISTAT_DSCE|ISTAT_DATAE|ISTAT_DPE|ISTAT_RDU|ISTAT_RFO|ISTAT_TFU)
+#define B44_IMASK 0x0024UL /* Interrupt Mask */
+#define IMASK_DEF (ISTAT_ERRORS | ISTAT_TO | ISTAT_RX | ISTAT_TX)
+#define B44_GPTIMER 0x0028UL /* General Purpose Timer */
+#define B44_ADDR_LO 0x0088UL /* ENET Address Lo (B0 only) */
+#define B44_ADDR_HI 0x008CUL /* ENET Address Hi (B0 only) */
+#define B44_FILT_ADDR 0x0090UL /* ENET Filter Address */
+#define B44_FILT_DATA 0x0094UL /* ENET Filter Data */
+#define B44_TXBURST 0x00A0UL /* TX Max Burst Length */
+#define B44_RXBURST 0x00A4UL /* RX Max Burst Length */
+#define B44_MAC_CTRL 0x00A8UL /* MAC Control */
+#define MAC_CTRL_CRC32_ENAB 0x00000001 /* CRC32 Generation Enable */
+#define MAC_CTRL_PHY_PDOWN 0x00000004 /* Onchip EPHY Powerdown */
+#define MAC_CTRL_PHY_EDET 0x00000008 /* Onchip EPHY Energy Detected */
+#define MAC_CTRL_PHY_LEDCTRL 0x000000e0 /* Onchip EPHY LED Control */
+#define MAC_CTRL_PHY_LEDCTRL_SHIFT 5
+#define B44_MAC_FLOW 0x00ACUL /* MAC Flow Control */
+#define MAC_FLOW_RX_HI_WATER 0x000000ff /* Receive FIFO HI Water Mark */
+#define MAC_FLOW_PAUSE_ENAB 0x00008000 /* Enable Pause Frame Generation */
+#define B44_RCV_LAZY 0x0100UL /* Lazy Interrupt Control */
+#define RCV_LAZY_TO_MASK 0x00ffffff /* Timeout */
+#define RCV_LAZY_FC_MASK 0xff000000 /* Frame Count */
+#define RCV_LAZY_FC_SHIFT 24
+#define B44_DMATX_CTRL 0x0200UL /* DMA TX Control */
+#define DMATX_CTRL_ENABLE 0x00000001 /* Enable */
+#define DMATX_CTRL_SUSPEND 0x00000002 /* Suepend Request */
+#define DMATX_CTRL_LPBACK 0x00000004 /* Loopback Enable */
+#define DMATX_CTRL_FAIRPRIOR 0x00000008 /* Fair Priority */
+#define DMATX_CTRL_FLUSH 0x00000010 /* Flush Request */
+#define B44_DMATX_ADDR 0x0204UL /* DMA TX Descriptor Ring Address */
+#define B44_DMATX_PTR 0x0208UL /* DMA TX Last Posted Descriptor */
+#define B44_DMATX_STAT 0x020CUL /* DMA TX Current Active Desc. + Status */
+#define DMATX_STAT_CDMASK 0x00000fff /* Current Descriptor Mask */
+#define DMATX_STAT_SMASK 0x0000f000 /* State Mask */
+#define DMATX_STAT_SDISABLED 0x00000000 /* State Disabled */
+#define DMATX_STAT_SACTIVE 0x00001000 /* State Active */
+#define DMATX_STAT_SIDLE 0x00002000 /* State Idle Wait */
+#define DMATX_STAT_SSTOPPED 0x00003000 /* State Stopped */
+#define DMATX_STAT_SSUSP 0x00004000 /* State Suspend Pending */
+#define DMATX_STAT_EMASK 0x000f0000 /* Error Mask */
+#define DMATX_STAT_ENONE 0x00000000 /* Error None */
+#define DMATX_STAT_EDPE 0x00010000 /* Error Desc. Protocol Error */
+#define DMATX_STAT_EDFU 0x00020000 /* Error Data FIFO Underrun */
+#define DMATX_STAT_EBEBR 0x00030000 /* Error Bus Error on Buffer Read */
+#define DMATX_STAT_EBEDA 0x00040000 /* Error Bus Error on Desc. Access */
+#define DMATX_STAT_FLUSHED 0x00100000 /* Flushed */
+#define B44_DMARX_CTRL 0x0210UL /* DMA RX Control */
+#define DMARX_CTRL_ENABLE 0x00000001 /* Enable */
+#define DMARX_CTRL_ROMASK 0x000000fe /* Receive Offset Mask */
+#define DMARX_CTRL_ROSHIFT 1 /* Receive Offset Shift */
+#define B44_DMARX_ADDR 0x0214UL /* DMA RX Descriptor Ring Address */
+#define B44_DMARX_PTR 0x0218UL /* DMA RX Last Posted Descriptor */
+#define B44_DMARX_STAT 0x021CUL /* DMA RX Current Active Desc. + Status */
+#define DMARX_STAT_CDMASK 0x00000fff /* Current Descriptor Mask */
+#define DMARX_STAT_SMASK 0x0000f000 /* State Mask */
+#define DMARX_STAT_SDISABLED 0x00000000 /* State Disabled */
+#define DMARX_STAT_SACTIVE 0x00001000 /* State Active */
+#define DMARX_STAT_SIDLE 0x00002000 /* State Idle Wait */
+#define DMARX_STAT_SSTOPPED 0x00003000 /* State Stopped */
+#define DMARX_STAT_EMASK 0x000f0000 /* Error Mask */
+#define DMARX_STAT_ENONE 0x00000000 /* Error None */
+#define DMARX_STAT_EDPE 0x00010000 /* Error Desc. Protocol Error */
+#define DMARX_STAT_EDFO 0x00020000 /* Error Data FIFO Overflow */
+#define DMARX_STAT_EBEBW 0x00030000 /* Error Bus Error on Buffer Write */
+#define DMARX_STAT_EBEDA 0x00040000 /* Error Bus Error on Desc. Access */
+#define B44_DMAFIFO_AD 0x0220UL /* DMA FIFO Diag Address */
+#define DMAFIFO_AD_OMASK 0x0000ffff /* Offset Mask */
+#define DMAFIFO_AD_SMASK 0x000f0000 /* Select Mask */
+#define DMAFIFO_AD_SXDD 0x00000000 /* Select Transmit DMA Data */
+#define DMAFIFO_AD_SXDP 0x00010000 /* Select Transmit DMA Pointers */
+#define DMAFIFO_AD_SRDD 0x00040000 /* Select Receive DMA Data */
+#define DMAFIFO_AD_SRDP 0x00050000 /* Select Receive DMA Pointers */
+#define DMAFIFO_AD_SXFD 0x00080000 /* Select Transmit FIFO Data */
+#define DMAFIFO_AD_SXFP 0x00090000 /* Select Transmit FIFO Pointers */
+#define DMAFIFO_AD_SRFD 0x000c0000 /* Select Receive FIFO Data */
+#define DMAFIFO_AD_SRFP 0x000c0000 /* Select Receive FIFO Pointers */
+#define B44_DMAFIFO_LO 0x0224UL /* DMA FIFO Diag Low Data */
+#define B44_DMAFIFO_HI 0x0228UL /* DMA FIFO Diag High Data */
+#define B44_RXCONFIG 0x0400UL /* EMAC RX Config */
+#define RXCONFIG_DBCAST 0x00000001 /* Disable Broadcast */
+#define RXCONFIG_ALLMULTI 0x00000002 /* Accept All Multicast */
+#define RXCONFIG_NORX_WHILE_TX 0x00000004 /* Receive Disable While Transmitting */
+#define RXCONFIG_PROMISC 0x00000008 /* Promiscuous Enable */
+#define RXCONFIG_LPBACK 0x00000010 /* Loopback Enable */
+#define RXCONFIG_FLOW 0x00000020 /* Flow Control Enable */
+#define RXCONFIG_FLOW_ACCEPT 0x00000040 /* Accept Unicast Flow Control Frame */
+#define RXCONFIG_RFILT 0x00000080 /* Reject Filter */
+#define RXCONFIG_CAM_ABSENT 0x00000100 /* CAM Absent */
+#define B44_RXMAXLEN 0x0404UL /* EMAC RX Max Packet Length */
+#define B44_TXMAXLEN 0x0408UL /* EMAC TX Max Packet Length */
+#define B44_MDIO_CTRL 0x0410UL /* EMAC MDIO Control */
+#define MDIO_CTRL_MAXF_MASK 0x0000007f /* MDC Frequency */
+#define MDIO_CTRL_PREAMBLE 0x00000080 /* MII Preamble Enable */
+#define B44_MDIO_DATA 0x0414UL /* EMAC MDIO Data */
+#define MDIO_DATA_DATA 0x0000ffff /* R/W Data */
+#define MDIO_DATA_TA_MASK 0x00030000 /* Turnaround Value */
+#define MDIO_DATA_TA_SHIFT 16
+#define MDIO_TA_VALID 2
+#define MDIO_DATA_RA_MASK 0x007c0000 /* Register Address */
+#define MDIO_DATA_RA_SHIFT 18
+#define MDIO_DATA_PMD_MASK 0x0f800000 /* Physical Media Device */
+#define MDIO_DATA_PMD_SHIFT 23
+#define MDIO_DATA_OP_MASK 0x30000000 /* Opcode */
+#define MDIO_DATA_OP_SHIFT 28
+#define MDIO_OP_WRITE 1
+#define MDIO_OP_READ 2
+#define MDIO_DATA_SB_MASK 0xc0000000 /* Start Bits */
+#define MDIO_DATA_SB_SHIFT 30
+#define MDIO_DATA_SB_START 0x40000000 /* Start Of Frame */
+#define B44_EMAC_IMASK 0x0418UL /* EMAC Interrupt Mask */
+#define B44_EMAC_ISTAT 0x041CUL /* EMAC Interrupt Status */
+#define EMAC_INT_MII 0x00000001 /* MII MDIO Interrupt */
+#define EMAC_INT_MIB 0x00000002 /* MIB Interrupt */
+#define EMAC_INT_FLOW 0x00000003 /* Flow Control Interrupt */
+#define B44_CAM_DATA_LO 0x0420UL /* EMAC CAM Data Low */
+#define B44_CAM_DATA_HI 0x0424UL /* EMAC CAM Data High */
+#define CAM_DATA_HI_VALID 0x00010000 /* Valid Bit */
+#define B44_CAM_CTRL 0x0428UL /* EMAC CAM Control */
+#define CAM_CTRL_ENABLE 0x00000001 /* CAM Enable */
+#define CAM_CTRL_MSEL 0x00000002 /* Mask Select */
+#define CAM_CTRL_READ 0x00000004 /* Read */
+#define CAM_CTRL_WRITE 0x00000008 /* Read */
+#define CAM_CTRL_INDEX_MASK 0x003f0000 /* Index Mask */
+#define CAM_CTRL_INDEX_SHIFT 16
+#define CAM_CTRL_BUSY 0x80000000 /* CAM Busy */
+#define B44_ENET_CTRL 0x042CUL /* EMAC ENET Control */
+#define ENET_CTRL_ENABLE 0x00000001 /* EMAC Enable */
+#define ENET_CTRL_DISABLE 0x00000002 /* EMAC Disable */
+#define ENET_CTRL_SRST 0x00000004 /* EMAC Soft Reset */
+#define ENET_CTRL_EPSEL 0x00000008 /* External PHY Select */
+#define B44_TX_CTRL 0x0430UL /* EMAC TX Control */
+#define TX_CTRL_DUPLEX 0x00000001 /* Full Duplex */
+#define TX_CTRL_FMODE 0x00000002 /* Flow Mode */
+#define TX_CTRL_SBENAB 0x00000004 /* Single Backoff Enable */
+#define TX_CTRL_SMALL_SLOT 0x00000008 /* Small Slottime */
+#define B44_TX_WMARK 0x0434UL /* EMAC TX Watermark */
+#define B44_MIB_CTRL 0x0438UL /* EMAC MIB Control */
+#define MIB_CTRL_CLR_ON_READ 0x00000001 /* Autoclear on Read */
+#define B44_TX_GOOD_O 0x0500UL /* MIB TX Good Octets */
+#define B44_TX_GOOD_P 0x0504UL /* MIB TX Good Packets */
+#define B44_TX_O 0x0508UL /* MIB TX Octets */
+#define B44_TX_P 0x050CUL /* MIB TX Packets */
+#define B44_TX_BCAST 0x0510UL /* MIB TX Broadcast Packets */
+#define B44_TX_MCAST 0x0514UL /* MIB TX Multicast Packets */
+#define B44_TX_64 0x0518UL /* MIB TX <= 64 byte Packets */
+#define B44_TX_65_127 0x051CUL /* MIB TX 65 to 127 byte Packets */
+#define B44_TX_128_255 0x0520UL /* MIB TX 128 to 255 byte Packets */
+#define B44_TX_256_511 0x0524UL /* MIB TX 256 to 511 byte Packets */
+#define B44_TX_512_1023 0x0528UL /* MIB TX 512 to 1023 byte Packets */
+#define B44_TX_1024_MAX 0x052CUL /* MIB TX 1024 to max byte Packets */
+#define B44_TX_JABBER 0x0530UL /* MIB TX Jabber Packets */
+#define B44_TX_OSIZE 0x0534UL /* MIB TX Oversize Packets */
+#define B44_TX_FRAG 0x0538UL /* MIB TX Fragment Packets */
+#define B44_TX_URUNS 0x053CUL /* MIB TX Underruns */
+#define B44_TX_TCOLS 0x0540UL /* MIB TX Total Collisions */
+#define B44_TX_SCOLS 0x0544UL /* MIB TX Single Collisions */
+#define B44_TX_MCOLS 0x0548UL /* MIB TX Multiple Collisions */
+#define B44_TX_ECOLS 0x054CUL /* MIB TX Excessive Collisions */
+#define B44_TX_LCOLS 0x0550UL /* MIB TX Late Collisions */
+#define B44_TX_DEFERED 0x0554UL /* MIB TX Defered Packets */
+#define B44_TX_CLOST 0x0558UL /* MIB TX Carrier Lost */
+#define B44_TX_PAUSE 0x055CUL /* MIB TX Pause Packets */
+#define B44_RX_GOOD_O 0x0580UL /* MIB RX Good Octets */
+#define B44_RX_GOOD_P 0x0584UL /* MIB RX Good Packets */
+#define B44_RX_O 0x0588UL /* MIB RX Octets */
+#define B44_RX_P 0x058CUL /* MIB RX Packets */
+#define B44_RX_BCAST 0x0590UL /* MIB RX Broadcast Packets */
+#define B44_RX_MCAST 0x0594UL /* MIB RX Multicast Packets */
+#define B44_RX_64 0x0598UL /* MIB RX <= 64 byte Packets */
+#define B44_RX_65_127 0x059CUL /* MIB RX 65 to 127 byte Packets */
+#define B44_RX_128_255 0x05A0UL /* MIB RX 128 to 255 byte Packets */
+#define B44_RX_256_511 0x05A4UL /* MIB RX 256 to 511 byte Packets */
+#define B44_RX_512_1023 0x05A8UL /* MIB RX 512 to 1023 byte Packets */
+#define B44_RX_1024_MAX 0x05ACUL /* MIB RX 1024 to max byte Packets */
+#define B44_RX_JABBER 0x05B0UL /* MIB RX Jabber Packets */
+#define B44_RX_OSIZE 0x05B4UL /* MIB RX Oversize Packets */
+#define B44_RX_FRAG 0x05B8UL /* MIB RX Fragment Packets */
+#define B44_RX_MISS 0x05BCUL /* MIB RX Missed Packets */
+#define B44_RX_CRCA 0x05C0UL /* MIB RX CRC Align Errors */
+#define B44_RX_USIZE 0x05C4UL /* MIB RX Undersize Packets */
+#define B44_RX_CRC 0x05C8UL /* MIB RX CRC Errors */
+#define B44_RX_ALIGN 0x05CCUL /* MIB RX Align Errors */
+#define B44_RX_SYM 0x05D0UL /* MIB RX Symbol Errors */
+#define B44_RX_PAUSE 0x05D4UL /* MIB RX Pause Packets */
+#define B44_RX_NPAUSE 0x05D8UL /* MIB RX Non-Pause Packets */
+
+/* 4400 PHY registers */
+#define B44_MII_AUXCTRL 24 /* Auxiliary Control */
+#define MII_AUXCTRL_DUPLEX 0x0001 /* Full Duplex */
+#define MII_AUXCTRL_SPEED 0x0002 /* 1=100Mbps, 0=10Mbps */
+#define MII_AUXCTRL_FORCED 0x0004 /* Forced 10/100 */
+#define B44_MII_ALEDCTRL 26 /* Activity LED */
+#define MII_ALEDCTRL_ALLMSK 0x7fff
+#define B44_MII_TLEDCTRL 27 /* Traffic Meter LED */
+#define MII_TLEDCTRL_ENABLE 0x0040
+
+struct dma_desc {
+ __le32 ctrl;
+ __le32 addr;
+};
+
+/* There are only 12 bits in the DMA engine for descriptor offsetting
+ * so the table must be aligned on a boundary of this.
+ */
+#define DMA_TABLE_BYTES 4096
+
+#define DESC_CTRL_LEN 0x00001fff
+#define DESC_CTRL_CMASK 0x0ff00000 /* Core specific bits */
+#define DESC_CTRL_EOT 0x10000000 /* End of Table */
+#define DESC_CTRL_IOC 0x20000000 /* Interrupt On Completion */
+#define DESC_CTRL_EOF 0x40000000 /* End of Frame */
+#define DESC_CTRL_SOF 0x80000000 /* Start of Frame */
+
+#define RX_COPY_THRESHOLD 256
+
+struct rx_header {
+ __le16 len;
+ __le16 flags;
+ __le16 pad[12];
+};
+#define RX_HEADER_LEN 28
+
+#define RX_FLAG_OFIFO 0x00000001 /* FIFO Overflow */
+#define RX_FLAG_CRCERR 0x00000002 /* CRC Error */
+#define RX_FLAG_SERR 0x00000004 /* Receive Symbol Error */
+#define RX_FLAG_ODD 0x00000008 /* Frame has odd number of nibbles */
+#define RX_FLAG_LARGE 0x00000010 /* Frame is > RX MAX Length */
+#define RX_FLAG_MCAST 0x00000020 /* Dest is Multicast Address */
+#define RX_FLAG_BCAST 0x00000040 /* Dest is Broadcast Address */
+#define RX_FLAG_MISS 0x00000080 /* Received due to promisc mode */
+#define RX_FLAG_LAST 0x00000800 /* Last buffer in frame */
+#define RX_FLAG_ERRORS (RX_FLAG_ODD | RX_FLAG_SERR | RX_FLAG_CRCERR | RX_FLAG_OFIFO)
+
+struct ring_info {
+ struct sk_buff *skb;
+ dma_addr_t mapping;
+};
+
+#define B44_MCAST_TABLE_SIZE 32
+#define B44_PHY_ADDR_NO_PHY 30
+#define B44_MDC_RATIO 5000000
+
+#define B44_STAT_REG_DECLARE \
+ _B44(tx_good_octets) \
+ _B44(tx_good_pkts) \
+ _B44(tx_octets) \
+ _B44(tx_pkts) \
+ _B44(tx_broadcast_pkts) \
+ _B44(tx_multicast_pkts) \
+ _B44(tx_len_64) \
+ _B44(tx_len_65_to_127) \
+ _B44(tx_len_128_to_255) \
+ _B44(tx_len_256_to_511) \
+ _B44(tx_len_512_to_1023) \
+ _B44(tx_len_1024_to_max) \
+ _B44(tx_jabber_pkts) \
+ _B44(tx_oversize_pkts) \
+ _B44(tx_fragment_pkts) \
+ _B44(tx_underruns) \
+ _B44(tx_total_cols) \
+ _B44(tx_single_cols) \
+ _B44(tx_multiple_cols) \
+ _B44(tx_excessive_cols) \
+ _B44(tx_late_cols) \
+ _B44(tx_defered) \
+ _B44(tx_carrier_lost) \
+ _B44(tx_pause_pkts) \
+ _B44(rx_good_octets) \
+ _B44(rx_good_pkts) \
+ _B44(rx_octets) \
+ _B44(rx_pkts) \
+ _B44(rx_broadcast_pkts) \
+ _B44(rx_multicast_pkts) \
+ _B44(rx_len_64) \
+ _B44(rx_len_65_to_127) \
+ _B44(rx_len_128_to_255) \
+ _B44(rx_len_256_to_511) \
+ _B44(rx_len_512_to_1023) \
+ _B44(rx_len_1024_to_max) \
+ _B44(rx_jabber_pkts) \
+ _B44(rx_oversize_pkts) \
+ _B44(rx_fragment_pkts) \
+ _B44(rx_missed_pkts) \
+ _B44(rx_crc_align_errs) \
+ _B44(rx_undersize) \
+ _B44(rx_crc_errs) \
+ _B44(rx_align_errs) \
+ _B44(rx_symbol_errs) \
+ _B44(rx_pause_pkts) \
+ _B44(rx_nonpause_pkts)
+
+/* SW copy of device statistics, kept up to date by periodic timer
+ * which probes HW values. Check b44_stats_update if you mess with
+ * the layout
+ */
+struct b44_hw_stats {
+#define _B44(x) u32 x;
+B44_STAT_REG_DECLARE
+#undef _B44
+};
+
+struct ssb_device;
+
+struct b44 {
+ spinlock_t lock;
+
+ u32 imask, istat;
+
+ struct dma_desc *rx_ring, *tx_ring;
+
+ u32 tx_prod, tx_cons;
+ u32 rx_prod, rx_cons;
+
+ struct ring_info *rx_buffers;
+ struct ring_info *tx_buffers;
+
+ struct napi_struct napi;
+
+ u32 dma_offset;
+ u32 flags;
+#define B44_FLAG_B0_ANDLATER 0x00000001
+#define B44_FLAG_BUGGY_TXPTR 0x00000002
+#define B44_FLAG_REORDER_BUG 0x00000004
+#define B44_FLAG_PAUSE_AUTO 0x00008000
+#define B44_FLAG_FULL_DUPLEX 0x00010000
+#define B44_FLAG_100_BASE_T 0x00020000
+#define B44_FLAG_TX_PAUSE 0x00040000
+#define B44_FLAG_RX_PAUSE 0x00080000
+#define B44_FLAG_FORCE_LINK 0x00100000
+#define B44_FLAG_ADV_10HALF 0x01000000
+#define B44_FLAG_ADV_10FULL 0x02000000
+#define B44_FLAG_ADV_100HALF 0x04000000
+#define B44_FLAG_ADV_100FULL 0x08000000
+#define B44_FLAG_INTERNAL_PHY 0x10000000
+#define B44_FLAG_RX_RING_HACK 0x20000000
+#define B44_FLAG_TX_RING_HACK 0x40000000
+#define B44_FLAG_WOL_ENABLE 0x80000000
+
+ u32 msg_enable;
+
+ struct timer_list timer;
+
+ struct b44_hw_stats hw_stats;
+
+ struct ssb_device *sdev;
+ struct net_device *dev;
+
+ dma_addr_t rx_ring_dma, tx_ring_dma;
+
+ u32 rx_pending;
+ u32 tx_pending;
+ u8 phy_addr;
+ u8 force_copybreak;
+ struct mii_if_info mii_if;
+};
+
+#endif /* _B44_H */
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
new file mode 100644
index 0000000..7d96e2e
--- /dev/null
+++ b/drivers/net/usb/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for USB Network drivers
+#
+
+obj-$(CONFIG_USB_NET_COMPAT_CDCETHER) += cdc_ether.o
+obj-$(CONFIG_USB_NET_COMPAT_RNDIS_HOST) += rndis_host.o
+obj-$(CONFIG_USB_COMPAT_USBNET) += usbnet.o
+
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
new file mode 100644
index 0000000..95c8d23
--- /dev/null
+++ b/drivers/net/usb/cdc_ether.c
@@ -0,0 +1,636 @@
+/*
+ * CDC Ethernet based networking peripherals
+ * 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 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
+ */
+
+// #define DEBUG // error path messages, extra info
+// #define VERBOSE // more; success messages
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/usbnet.h>
+
+
+#if defined(CONFIG_USB_NET_COMPAT_RNDIS_HOST) || defined(CONFIG_USB_NET_COMPAT_RNDIS_HOST_MODULE)
+
+static int is_rndis(struct usb_interface_descriptor *desc)
+{
+ return (desc->bInterfaceClass == USB_CLASS_COMM &&
+ desc->bInterfaceSubClass == 2 &&
+ desc->bInterfaceProtocol == 0xff);
+}
+
+static int is_activesync(struct usb_interface_descriptor *desc)
+{
+ return (desc->bInterfaceClass == USB_CLASS_MISC &&
+ desc->bInterfaceSubClass == 1 &&
+ desc->bInterfaceProtocol == 1);
+}
+
+static int is_wireless_rndis(struct usb_interface_descriptor *desc)
+{
+ return (desc->bInterfaceClass == USB_CLASS_WIRELESS_CONTROLLER &&
+ desc->bInterfaceSubClass == 1 &&
+ desc->bInterfaceProtocol == 3);
+}
+
+#else
+
+#define is_rndis(desc) 0
+#define is_activesync(desc) 0
+#define is_wireless_rndis(desc) 0
+
+#endif
+
+static const u8 mbm_guid[16] = {
+ 0xa3, 0x17, 0xa8, 0x8b, 0x04, 0x5e, 0x4f, 0x01,
+ 0xa6, 0x07, 0xc0, 0xff, 0xcb, 0x7e, 0x39, 0x2a,
+};
+
+/*
+ * probes control interface, claims data interface, collects the bulk
+ * endpoints, activates data interface (if needed), maybe sets MTU.
+ * all pure cdc, except for certain firmware workarounds, and knowing
+ * that rndis uses one different rule.
+ */
+int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+ u8 *buf = intf->cur_altsetting->extra;
+ int len = intf->cur_altsetting->extralen;
+ struct usb_interface_descriptor *d;
+ struct cdc_state *info = (void *) &dev->data;
+ int status;
+ int rndis;
+ struct usb_driver *driver = driver_of(intf);
+ struct usb_cdc_mdlm_desc *desc = NULL;
+ struct usb_cdc_mdlm_detail_desc *detail = NULL;
+
+ if (sizeof dev->data < sizeof *info)
+ return -EDOM;
+
+ /* expect strict spec conformance for the descriptors, but
+ * cope with firmware which stores them in the wrong place
+ */
+ if (len == 0 && dev->udev->actconfig->extralen) {
+ /* Motorola SB4100 (and others: Brad Hards says it's
+ * from a Broadcom design) put CDC descriptors here
+ */
+ buf = dev->udev->actconfig->extra;
+ len = dev->udev->actconfig->extralen;
+ dev_dbg(&intf->dev, "CDC descriptors on config\n");
+ }
+
+ /* Maybe CDC descriptors are after the endpoint? This bug has
+ * been seen on some 2Wire Inc RNDIS-ish products.
+ */
+ if (len == 0) {
+ struct usb_host_endpoint *hep;
+
+ hep = intf->cur_altsetting->endpoint;
+ if (hep) {
+ buf = hep->extra;
+ len = hep->extralen;
+ }
+ if (len)
+ dev_dbg(&intf->dev,
+ "CDC descriptors on endpoint\n");
+ }
+
+ /* this assumes that if there's a non-RNDIS vendor variant
+ * of cdc-acm, it'll fail RNDIS requests cleanly.
+ */
+ rndis = (is_rndis(&intf->cur_altsetting->desc) ||
+ is_activesync(&intf->cur_altsetting->desc) ||
+ is_wireless_rndis(&intf->cur_altsetting->desc));
+
+ memset(info, 0, sizeof *info);
+ info->control = intf;
+ while (len > 3) {
+ if (buf [1] != USB_DT_CS_INTERFACE)
+ goto next_desc;
+
+ /* use bDescriptorSubType to identify the CDC descriptors.
+ * We expect devices with CDC header and union descriptors.
+ * For CDC Ethernet we need the ethernet descriptor.
+ * For RNDIS, ignore two (pointless) CDC modem descriptors
+ * in favor of a complicated OID-based RPC scheme doing what
+ * CDC Ethernet achieves with a simple descriptor.
+ */
+ switch (buf [2]) {
+ case USB_CDC_HEADER_TYPE:
+ if (info->header) {
+ dev_dbg(&intf->dev, "extra CDC header\n");
+ goto bad_desc;
+ }
+ info->header = (void *) buf;
+ if (info->header->bLength != sizeof *info->header) {
+ dev_dbg(&intf->dev, "CDC header len %u\n",
+ info->header->bLength);
+ goto bad_desc;
+ }
+ break;
+ case USB_CDC_ACM_TYPE:
+ /* paranoia: disambiguate a "real" vendor-specific
+ * modem interface from an RNDIS non-modem.
+ */
+ if (rndis) {
+ struct usb_cdc_acm_descriptor *acm;
+
+ acm = (void *) buf;
+ if (acm->bmCapabilities) {
+ dev_dbg(&intf->dev,
+ "ACM capabilities %02x, "
+ "not really RNDIS?\n",
+ acm->bmCapabilities);
+ goto bad_desc;
+ }
+ }
+ break;
+ case USB_CDC_UNION_TYPE:
+ if (info->u) {
+ dev_dbg(&intf->dev, "extra CDC union\n");
+ goto bad_desc;
+ }
+ info->u = (void *) buf;
+ if (info->u->bLength != sizeof *info->u) {
+ dev_dbg(&intf->dev, "CDC union len %u\n",
+ info->u->bLength);
+ goto bad_desc;
+ }
+
+ /* we need a master/control interface (what we're
+ * probed with) and a slave/data interface; union
+ * descriptors sort this all out.
+ */
+ info->control = usb_ifnum_to_if(dev->udev,
+ info->u->bMasterInterface0);
+ info->data = usb_ifnum_to_if(dev->udev,
+ info->u->bSlaveInterface0);
+ if (!info->control || !info->data) {
+ dev_dbg(&intf->dev,
+ "master #%u/%p slave #%u/%p\n",
+ info->u->bMasterInterface0,
+ info->control,
+ info->u->bSlaveInterface0,
+ info->data);
+ goto bad_desc;
+ }
+ if (info->control != intf) {
+ dev_dbg(&intf->dev, "bogus CDC Union\n");
+ /* Ambit USB Cable Modem (and maybe others)
+ * interchanges master and slave interface.
+ */
+ if (info->data == intf) {
+ info->data = info->control;
+ info->control = intf;
+ } else
+ goto bad_desc;
+ }
+
+ /* a data interface altsetting does the real i/o */
+ d = &info->data->cur_altsetting->desc;
+ if (d->bInterfaceClass != USB_CLASS_CDC_DATA) {
+ dev_dbg(&intf->dev, "slave class %u\n",
+ d->bInterfaceClass);
+ goto bad_desc;
+ }
+ break;
+ case USB_CDC_ETHERNET_TYPE:
+ if (info->ether) {
+ dev_dbg(&intf->dev, "extra CDC ether\n");
+ goto bad_desc;
+ }
+ info->ether = (void *) buf;
+ if (info->ether->bLength != sizeof *info->ether) {
+ dev_dbg(&intf->dev, "CDC ether len %u\n",
+ info->ether->bLength);
+ goto bad_desc;
+ }
+ dev->hard_mtu = le16_to_cpu(
+ info->ether->wMaxSegmentSize);
+ /* because of Zaurus, we may be ignoring the host
+ * side link address we were given.
+ */
+ break;
+ case USB_CDC_MDLM_TYPE:
+ if (desc) {
+ dev_dbg(&intf->dev, "extra MDLM descriptor\n");
+ goto bad_desc;
+ }
+
+ desc = (void *)buf;
+
+ if (desc->bLength != sizeof(*desc))
+ goto bad_desc;
+
+ if (memcmp(&desc->bGUID, mbm_guid, 16))
+ goto bad_desc;
+ break;
+ case USB_CDC_MDLM_DETAIL_TYPE:
+ if (detail) {
+ dev_dbg(&intf->dev, "extra MDLM detail descriptor\n");
+ goto bad_desc;
+ }
+
+ detail = (void *)buf;
+
+ if (detail->bGuidDescriptorType == 0) {
+ if (detail->bLength < (sizeof(*detail) + 1))
+ goto bad_desc;
+ } else
+ goto bad_desc;
+ break;
+ }
+next_desc:
+ len -= buf [0]; /* bLength */
+ buf += buf [0];
+ }
+
+ /* Microsoft ActiveSync based and some regular RNDIS devices lack the
+ * CDC descriptors, so we'll hard-wire the interfaces and not check
+ * for descriptors.
+ */
+ if (rndis && !info->u) {
+ info->control = usb_ifnum_to_if(dev->udev, 0);
+ info->data = usb_ifnum_to_if(dev->udev, 1);
+ if (!info->control || !info->data) {
+ dev_dbg(&intf->dev,
+ "rndis: master #0/%p slave #1/%p\n",
+ info->control,
+ info->data);
+ goto bad_desc;
+ }
+
+ } else if (!info->header || !info->u || (!rndis && !info->ether)) {
+ dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n",
+ info->header ? "" : "header ",
+ info->u ? "" : "union ",
+ info->ether ? "" : "ether ");
+ goto bad_desc;
+ }
+
+ /* claim data interface and set it up ... with side effects.
+ * network traffic can't flow until an altsetting is enabled.
+ */
+ status = usb_driver_claim_interface(driver, info->data, dev);
+ if (status < 0)
+ return status;
+ status = usbnet_get_endpoints(dev, info->data);
+ if (status < 0) {
+ /* ensure immediate exit from usbnet_disconnect */
+ usb_set_intfdata(info->data, NULL);
+ usb_driver_release_interface(driver, info->data);
+ return status;
+ }
+
+ /* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */
+ dev->status = NULL;
+ if (info->control->cur_altsetting->desc.bNumEndpoints == 1) {
+ struct usb_endpoint_descriptor *desc;
+
+ dev->status = &info->control->cur_altsetting->endpoint [0];
+ desc = &dev->status->desc;
+ if (!usb_endpoint_is_int_in(desc) ||
+ (le16_to_cpu(desc->wMaxPacketSize)
+ < sizeof(struct usb_cdc_notification)) ||
+ !desc->bInterval) {
+ dev_dbg(&intf->dev, "bad notification endpoint\n");
+ dev->status = NULL;
+ }
+ }
+ if (rndis && !dev->status) {
+ dev_dbg(&intf->dev, "missing RNDIS status endpoint\n");
+ usb_set_intfdata(info->data, NULL);
+ usb_driver_release_interface(driver, info->data);
+ return -ENODEV;
+ }
+ return 0;
+
+bad_desc:
+ dev_info(&dev->udev->dev, "bad CDC descriptors\n");
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(usbnet_generic_cdc_bind);
+
+void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+ struct cdc_state *info = (void *) &dev->data;
+ struct usb_driver *driver = driver_of(intf);
+
+ /* disconnect master --> disconnect slave */
+ if (intf == info->control && info->data) {
+ /* ensure immediate exit from usbnet_disconnect */
+ usb_set_intfdata(info->data, NULL);
+ usb_driver_release_interface(driver, info->data);
+ info->data = NULL;
+ }
+
+ /* and vice versa (just in case) */
+ else if (intf == info->data && info->control) {
+ /* ensure immediate exit from usbnet_disconnect */
+ usb_set_intfdata(info->control, NULL);
+ usb_driver_release_interface(driver, info->control);
+ info->control = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(usbnet_cdc_unbind);
+
+/*-------------------------------------------------------------------------
+ *
+ * Communications Device Class, Ethernet Control model
+ *
+ * Takes two interfaces. The DATA interface is inactive till an altsetting
+ * is selected. Configuration data includes class descriptors. There's
+ * an optional status endpoint on the control interface.
+ *
+ * This should interop with whatever the 2.4 "CDCEther.c" driver
+ * (by Brad Hards) talked with, with more functionality.
+ *
+ *-------------------------------------------------------------------------*/
+
+static void dumpspeed(struct usbnet *dev, __le32 *speeds)
+{
+ netif_info(dev, timer, dev->net,
+ "link speeds: %u kbps up, %u kbps down\n",
+ __le32_to_cpu(speeds[0]) / 1000,
+ __le32_to_cpu(speeds[1]) / 1000);
+}
+
+void usbnet_cdc_status(struct usbnet *dev, struct urb *urb)
+{
+ struct usb_cdc_notification *event;
+
+ if (urb->actual_length < sizeof *event)
+ return;
+
+ /* SPEED_CHANGE can get split into two 8-byte packets */
+ if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) {
+ dumpspeed(dev, (__le32 *) urb->transfer_buffer);
+ return;
+ }
+
+ event = urb->transfer_buffer;
+ switch (event->bNotificationType) {
+ case USB_CDC_NOTIFY_NETWORK_CONNECTION:
+ netif_dbg(dev, timer, dev->net, "CDC: carrier %s\n",
+ event->wValue ? "on" : "off");
+ if (event->wValue)
+ netif_carrier_on(dev->net);
+ else
+ netif_carrier_off(dev->net);
+ break;
+ case USB_CDC_NOTIFY_SPEED_CHANGE: /* tx/rx rates */
+ netif_dbg(dev, timer, dev->net, "CDC: speed change (len %d)\n",
+ urb->actual_length);
+ if (urb->actual_length != (sizeof *event + 8))
+ set_bit(EVENT_STS_SPLIT, &dev->flags);
+ else
+ dumpspeed(dev, (__le32 *) &event[1]);
+ break;
+ /* USB_CDC_NOTIFY_RESPONSE_AVAILABLE can happen too (e.g. RNDIS),
+ * but there are no standard formats for the response data.
+ */
+ default:
+ netdev_err(dev->net, "CDC: unexpected notification %02x!\n",
+ event->bNotificationType);
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(usbnet_cdc_status);
+
+int usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+ int status;
+ struct cdc_state *info = (void *) &dev->data;
+
+ status = usbnet_generic_cdc_bind(dev, intf);
+ if (status < 0)
+ return status;
+
+ status = usbnet_get_ethernet_addr(dev, info->ether->iMACAddress);
+ if (status < 0) {
+ usb_set_intfdata(info->data, NULL);
+ usb_driver_release_interface(driver_of(intf), info->data);
+ return status;
+ }
+
+ /* FIXME cdc-ether has some multicast code too, though it complains
+ * in routine cases. info->ether describes the multicast support.
+ * Implement that here, manipulating the cdc filter as needed.
+ */
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usbnet_cdc_bind);
+
+static int cdc_manage_power(struct usbnet *dev, int on)
+{
+ dev->intf->needs_remote_wakeup = on;
+ return 0;
+}
+
+static const struct driver_info cdc_info = {
+ .description = "CDC Ethernet Device",
+ .flags = FLAG_ETHER | FLAG_POINTTOPOINT,
+ // .check_connect = cdc_check_connect,
+ .bind = usbnet_cdc_bind,
+ .unbind = usbnet_cdc_unbind,
+ .status = usbnet_cdc_status,
+ .manage_power = cdc_manage_power,
+};
+
+static const struct driver_info wwan_info = {
+ .description = "Mobile Broadband Network Device",
+ .flags = FLAG_WWAN,
+ .bind = usbnet_cdc_bind,
+ .unbind = usbnet_cdc_unbind,
+ .status = usbnet_cdc_status,
+ .manage_power = cdc_manage_power,
+};
+
+/*-------------------------------------------------------------------------*/
+
+#define HUAWEI_VENDOR_ID 0x12D1
+
+static const struct usb_device_id products [] = {
+/*
+ * BLACKLIST !!
+ *
+ * First blacklist any products that are egregiously nonconformant
+ * with the CDC Ethernet specs. Minor braindamage we cope with; when
+ * they're not even trying, needing a separate driver is only the first
+ * of the differences to show up.
+ */
+
+#define ZAURUS_MASTER_INTERFACE \
+ .bInterfaceClass = USB_CLASS_COMM, \
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, \
+ .bInterfaceProtocol = USB_CDC_PROTO_NONE
+
+/* SA-1100 based Sharp Zaurus ("collie"), or compatible;
+ * wire-incompatible with true CDC Ethernet implementations.
+ * (And, it seems, needlessly so...)
+ */
+{
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x04DD,
+ .idProduct = 0x8004,
+ ZAURUS_MASTER_INTERFACE,
+ .driver_info = 0,
+},
+
+/* PXA-25x based Sharp Zaurii. Note that it seems some of these
+ * (later models especially) may have shipped only with firmware
+ * advertising false "CDC MDLM" compatibility ... but we're not
+ * clear which models did that, so for now let's assume the worst.
+ */
+{
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x04DD,
+ .idProduct = 0x8005, /* A-300 */
+ ZAURUS_MASTER_INTERFACE,
+ .driver_info = 0,
+}, {
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x04DD,
+ .idProduct = 0x8006, /* B-500/SL-5600 */
+ ZAURUS_MASTER_INTERFACE,
+ .driver_info = 0,
+}, {
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x04DD,
+ .idProduct = 0x8007, /* C-700 */
+ ZAURUS_MASTER_INTERFACE,
+ .driver_info = 0,
+}, {
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x04DD,
+ .idProduct = 0x9031, /* C-750 C-760 */
+ ZAURUS_MASTER_INTERFACE,
+ .driver_info = 0,
+}, {
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x04DD,
+ .idProduct = 0x9032, /* SL-6000 */
+ ZAURUS_MASTER_INTERFACE,
+ .driver_info = 0,
+}, {
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x04DD,
+ /* reported with some C860 units */
+ .idProduct = 0x9050, /* C-860 */
+ ZAURUS_MASTER_INTERFACE,
+ .driver_info = 0,
+},
+
+/* Olympus has some models with a Zaurus-compatible option.
+ * R-1000 uses a FreeScale i.MXL cpu (ARMv4T)
+ */
+{
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x07B4,
+ .idProduct = 0x0F02, /* R-1000 */
+ ZAURUS_MASTER_INTERFACE,
+ .driver_info = 0,
+},
+
+/* LG Electronics VL600 wants additional headers on every frame */
+{
+ USB_DEVICE_AND_INTERFACE_INFO(0x1004, 0x61aa, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long)&wwan_info,
+},
+
+/*
+ * WHITELIST!!!
+ *
+ * CDC Ether uses two interfaces, not necessarily consecutive.
+ * We match the main interface, ignoring the optional device
+ * class so we could handle devices that aren't exclusively
+ * CDC ether.
+ *
+ * NOTE: this match must come AFTER entries blacklisting devices
+ * because of bugs/quirks in a given product (like Zaurus, above).
+ */
+{
+ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,
+ USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long) &cdc_info,
+}, {
+ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM,
+ USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long)&wwan_info,
+
+}, {
+ /* Various Huawei modems with a network port like the UMG1831 */
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = HUAWEI_VENDOR_ID,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET,
+ .bInterfaceProtocol = 255,
+ .driver_info = (unsigned long)&wwan_info,
+},
+ { }, // END
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver cdc_driver = {
+ .name = "cdc_ether",
+ .id_table = products,
+ .probe = usbnet_probe,
+ .disconnect = usbnet_disconnect,
+ .suspend = usbnet_suspend,
+ .resume = usbnet_resume,
+ .reset_resume = usbnet_resume,
+ .supports_autosuspend = 1,
+};
+
+
+static int __init cdc_init(void)
+{
+ BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data)
+ < sizeof(struct cdc_state)));
+
+ return usb_register(&cdc_driver);
+}
+module_init(cdc_init);
+
+static void __exit cdc_exit(void)
+{
+ usb_deregister(&cdc_driver);
+}
+module_exit(cdc_exit);
+
+MODULE_AUTHOR("David Brownell");
+MODULE_DESCRIPTION("USB CDC Ethernet devices");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
new file mode 100644
index 0000000..f9dd754
--- /dev/null
+++ b/drivers/net/usb/rndis_host.c
@@ -0,0 +1,656 @@
+/*
+ * Host Side support for RNDIS Networking Links
+ * Copyright (C) 2005 by David Brownell
+ *
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/usbnet.h>
+#include <linux/usb/rndis_host.h>
+
+
+/*
+ * RNDIS is NDIS remoted over USB. It's a MSFT variant of CDC ACM ... of
+ * course ACM was intended for modems, not Ethernet links! USB's standard
+ * for Ethernet links is "CDC Ethernet", which is significantly simpler.
+ *
+ * NOTE that Microsoft's "RNDIS 1.0" specification is incomplete. Issues
+ * include:
+ * - Power management in particular relies on information that's scattered
+ * through other documentation, and which is incomplete or incorrect even
+ * there.
+ * - There are various undocumented protocol requirements, such as the
+ * need to send unused garbage in control-OUT messages.
+ * - In some cases, MS-Windows will emit undocumented requests; this
+ * matters more to peripheral implementations than host ones.
+ *
+ * Moreover there's a no-open-specs variant of RNDIS called "ActiveSync".
+ *
+ * For these reasons and others, ** USE OF RNDIS IS STRONGLY DISCOURAGED ** in
+ * favor of such non-proprietary alternatives as CDC Ethernet or the newer (and
+ * currently rare) "Ethernet Emulation Model" (EEM).
+ */
+
+/*
+ * RNDIS notifications from device: command completion; "reverse"
+ * keepalives; etc
+ */
+void rndis_status(struct usbnet *dev, struct urb *urb)
+{
+ netdev_dbg(dev->net, "rndis status urb, len %d stat %d\n",
+ urb->actual_length, urb->status);
+ // FIXME for keepalives, respond immediately (asynchronously)
+ // if not an RNDIS status, do like cdc_status(dev,urb) does
+}
+EXPORT_SYMBOL_GPL(rndis_status);
+
+/*
+ * RNDIS indicate messages.
+ */
+static void rndis_msg_indicate(struct usbnet *dev, struct rndis_indicate *msg,
+ int buflen)
+{
+ struct cdc_state *info = (void *)&dev->data;
+ struct device *udev = &info->control->dev;
+
+ if (dev->driver_info->indication) {
+ dev->driver_info->indication(dev, msg, buflen);
+ } else {
+ switch (msg->status) {
+ case RNDIS_STATUS_MEDIA_CONNECT:
+ dev_info(udev, "rndis media connect\n");
+ break;
+ case RNDIS_STATUS_MEDIA_DISCONNECT:
+ dev_info(udev, "rndis media disconnect\n");
+ break;
+ default:
+ dev_info(udev, "rndis indication: 0x%08x\n",
+ le32_to_cpu(msg->status));
+ }
+ }
+}
+
+/*
+ * RPC done RNDIS-style. Caller guarantees:
+ * - message is properly byteswapped
+ * - there's no other request pending
+ * - buf can hold up to 1KB response (required by RNDIS spec)
+ * On return, the first few entries are already byteswapped.
+ *
+ * Call context is likely probe(), before interface name is known,
+ * which is why we won't try to use it in the diagnostics.
+ */
+int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen)
+{
+ struct cdc_state *info = (void *) &dev->data;
+ struct usb_cdc_notification notification;
+ int master_ifnum;
+ int retval;
+ int partial;
+ unsigned count;
+ __le32 rsp;
+ u32 xid = 0, msg_len, request_id;
+
+ /* REVISIT when this gets called from contexts other than probe() or
+ * disconnect(): either serialize, or dispatch responses on xid
+ */
+
+ /* Issue the request; xid is unique, don't bother byteswapping it */
+ if (likely(buf->msg_type != RNDIS_MSG_HALT &&
+ buf->msg_type != RNDIS_MSG_RESET)) {
+ xid = dev->xid++;
+ if (!xid)
+ xid = dev->xid++;
+ buf->request_id = (__force __le32) xid;
+ }
+ master_ifnum = info->control->cur_altsetting->desc.bInterfaceNumber;
+ retval = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ USB_CDC_SEND_ENCAPSULATED_COMMAND,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, master_ifnum,
+ buf, le32_to_cpu(buf->msg_len),
+ RNDIS_CONTROL_TIMEOUT_MS);
+ if (unlikely(retval < 0 || xid == 0))
+ return retval;
+
+ /* Some devices don't respond on the control channel until
+ * polled on the status channel, so do that first. */
+ if (dev->driver_info->data & RNDIS_DRIVER_DATA_POLL_STATUS) {
+ retval = usb_interrupt_msg(
+ dev->udev,
+ usb_rcvintpipe(dev->udev,
+ dev->status->desc.bEndpointAddress),
+ ¬ification, sizeof(notification), &partial,
+ RNDIS_CONTROL_TIMEOUT_MS);
+ if (unlikely(retval < 0))
+ return retval;
+ }
+
+ /* Poll the control channel; the request probably completed immediately */
+ rsp = buf->msg_type | RNDIS_MSG_COMPLETION;
+ for (count = 0; count < 10; count++) {
+ memset(buf, 0, CONTROL_BUFFER_SIZE);
+ retval = usb_control_msg(dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0),
+ USB_CDC_GET_ENCAPSULATED_RESPONSE,
+ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, master_ifnum,
+ buf, buflen,
+ RNDIS_CONTROL_TIMEOUT_MS);
+ if (likely(retval >= 8)) {
+ msg_len = le32_to_cpu(buf->msg_len);
+ request_id = (__force u32) buf->request_id;
+ if (likely(buf->msg_type == rsp)) {
+ if (likely(request_id == xid)) {
+ if (unlikely(rsp == RNDIS_MSG_RESET_C))
+ return 0;
+ if (likely(RNDIS_STATUS_SUCCESS
+ == buf->status))
+ return 0;
+ dev_dbg(&info->control->dev,
+ "rndis reply status %08x\n",
+ le32_to_cpu(buf->status));
+ return -EL3RST;
+ }
+ dev_dbg(&info->control->dev,
+ "rndis reply id %d expected %d\n",
+ request_id, xid);
+ /* then likely retry */
+ } else switch (buf->msg_type) {
+ case RNDIS_MSG_INDICATE: /* fault/event */
+ rndis_msg_indicate(dev, (void *)buf, buflen);
+
+ break;
+ case RNDIS_MSG_KEEPALIVE: { /* ping */
+ struct rndis_keepalive_c *msg = (void *)buf;
+
+ msg->msg_type = RNDIS_MSG_KEEPALIVE_C;
+ msg->msg_len = cpu_to_le32(sizeof *msg);
+ msg->status = RNDIS_STATUS_SUCCESS;
+ retval = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ USB_CDC_SEND_ENCAPSULATED_COMMAND,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, master_ifnum,
+ msg, sizeof *msg,
+ RNDIS_CONTROL_TIMEOUT_MS);
+ if (unlikely(retval < 0))
+ dev_dbg(&info->control->dev,
+ "rndis keepalive err %d\n",
+ retval);
+ }
+ break;
+ default:
+ dev_dbg(&info->control->dev,
+ "unexpected rndis msg %08x len %d\n",
+ le32_to_cpu(buf->msg_type), msg_len);
+ }
+ } else {
+ /* device probably issued a protocol stall; ignore */
+ dev_dbg(&info->control->dev,
+ "rndis response error, code %d\n", retval);
+ }
+ msleep(20);
+ }
+ dev_dbg(&info->control->dev, "rndis response timeout\n");
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_GPL(rndis_command);
+
+/*
+ * rndis_query:
+ *
+ * Performs a query for @oid along with 0 or more bytes of payload as
+ * specified by @in_len. If @reply_len is not set to -1 then the reply
+ * length is checked against this value, resulting in an error if it
+ * doesn't match.
+ *
+ * NOTE: Adding a payload exactly or greater than the size of the expected
+ * response payload is an evident requirement MSFT added for ActiveSync.
+ *
+ * The only exception is for OIDs that return a variably sized response,
+ * in which case no payload should be added. This undocumented (and
+ * nonsensical!) issue was found by sniffing protocol requests from the
+ * ActiveSync 4.1 Windows driver.
+ */
+static int rndis_query(struct usbnet *dev, struct usb_interface *intf,
+ void *buf, __le32 oid, u32 in_len,
+ void **reply, int *reply_len)
+{
+ int retval;
+ union {
+ void *buf;
+ struct rndis_msg_hdr *header;
+ struct rndis_query *get;
+ struct rndis_query_c *get_c;
+ } u;
+ u32 off, len;
+
+ u.buf = buf;
+
+ memset(u.get, 0, sizeof *u.get + in_len);
+ u.get->msg_type = RNDIS_MSG_QUERY;
+ u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len);
+ u.get->oid = oid;
+ u.get->len = cpu_to_le32(in_len);
+ u.get->offset = cpu_to_le32(20);
+
+ retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE);
+ if (unlikely(retval < 0)) {
+ dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n",
+ oid, retval);
+ return retval;
+ }
+
+ off = le32_to_cpu(u.get_c->offset);
+ len = le32_to_cpu(u.get_c->len);
+ if (unlikely((8 + off + len) > CONTROL_BUFFER_SIZE))
+ goto response_error;
+
+ if (*reply_len != -1 && len != *reply_len)
+ goto response_error;
+
+ *reply = (unsigned char *) &u.get_c->request_id + off;
+ *reply_len = len;
+
+ return retval;
+
+response_error:
+ dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) "
+ "invalid response - off %d len %d\n",
+ oid, off, len);
+ return -EDOM;
+}
+
+/* same as usbnet_netdev_ops but MTU change not allowed */
+static const struct net_device_ops rndis_netdev_ops = {
+ .ndo_open = usbnet_open,
+ .ndo_stop = usbnet_stop,
+ .ndo_start_xmit = usbnet_start_xmit,
+ .ndo_tx_timeout = usbnet_tx_timeout,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+int
+generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags)
+{
+ int retval;
+ struct net_device *net = dev->net;
+ struct cdc_state *info = (void *) &dev->data;
+ union {
+ void *buf;
+ struct rndis_msg_hdr *header;
+ struct rndis_init *init;
+ struct rndis_init_c *init_c;
+ struct rndis_query *get;
+ struct rndis_query_c *get_c;
+ struct rndis_set *set;
+ struct rndis_set_c *set_c;
+ struct rndis_halt *halt;
+ } u;
+ u32 tmp;
+ __le32 phym_unspec, *phym;
+ int reply_len;
+ unsigned char *bp;
+
+ /* we can't rely on i/o from stack working, or stack allocation */
+ u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
+ if (!u.buf)
+ return -ENOMEM;
+ retval = usbnet_generic_cdc_bind(dev, intf);
+ if (retval < 0)
+ goto fail;
+
+ u.init->msg_type = RNDIS_MSG_INIT;
+ u.init->msg_len = cpu_to_le32(sizeof *u.init);
+ u.init->major_version = cpu_to_le32(1);
+ u.init->minor_version = cpu_to_le32(0);
+
+ /* max transfer (in spec) is 0x4000 at full speed, but for
+ * TX we'll stick to one Ethernet packet plus RNDIS framing.
+ * For RX we handle drivers that zero-pad to end-of-packet.
+ * Don't let userspace change these settings.
+ *
+ * NOTE: there still seems to be wierdness here, as if we need
+ * to do some more things to make sure WinCE targets accept this.
+ * They default to jumbograms of 8KB or 16KB, which is absurd
+ * for such low data rates and which is also more than Linux
+ * can usually expect to allocate for SKB data...
+ */
+ net->hard_header_len += sizeof (struct rndis_data_hdr);
+ dev->hard_mtu = net->mtu + net->hard_header_len;
+
+ dev->maxpacket = usb_maxpacket(dev->udev, dev->out, 1);
+ if (dev->maxpacket == 0) {
+ netif_dbg(dev, probe, dev->net,
+ "dev->maxpacket can't be 0\n");
+ retval = -EINVAL;
+ goto fail_and_release;
+ }
+
+ dev->rx_urb_size = dev->hard_mtu + (dev->maxpacket + 1);
+ dev->rx_urb_size &= ~(dev->maxpacket - 1);
+ u.init->max_transfer_size = cpu_to_le32(dev->rx_urb_size);
+
+ netdev_attach_ops(net, &rndis_netdev_ops);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29))
+ /* can't we remove this? */
+ net->change_mtu = NULL;
+#endif
+
+ retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE);
+ if (unlikely(retval < 0)) {
+ /* it might not even be an RNDIS device!! */
+ dev_err(&intf->dev, "RNDIS init failed, %d\n", retval);
+ goto fail_and_release;
+ }
+ tmp = le32_to_cpu(u.init_c->max_transfer_size);
+ if (tmp < dev->hard_mtu) {
+ if (tmp <= net->hard_header_len) {
+ dev_err(&intf->dev,
+ "dev can't take %u byte packets (max %u)\n",
+ dev->hard_mtu, tmp);
+ retval = -EINVAL;
+ goto halt_fail_and_release;
+ }
+ dev_warn(&intf->dev,
+ "dev can't take %u byte packets (max %u), "
+ "adjusting MTU to %u\n",
+ dev->hard_mtu, tmp, tmp - net->hard_header_len);
+ dev->hard_mtu = tmp;
+ net->mtu = dev->hard_mtu - net->hard_header_len;
+ }
+
+ /* REVISIT: peripheral "alignment" request is ignored ... */
+ dev_dbg(&intf->dev,
+ "hard mtu %u (%u from dev), rx buflen %Zu, align %d\n",
+ dev->hard_mtu, tmp, dev->rx_urb_size,
+ 1 << le32_to_cpu(u.init_c->packet_alignment));
+
+ /* module has some device initialization code needs to be done right
+ * after RNDIS_INIT */
+ if (dev->driver_info->early_init &&
+ dev->driver_info->early_init(dev) != 0)
+ goto halt_fail_and_release;
+
+ /* Check physical medium */
+ phym = NULL;
+ reply_len = sizeof *phym;
+ retval = rndis_query(dev, intf, u.buf, OID_GEN_PHYSICAL_MEDIUM,
+ 0, (void **) &phym, &reply_len);
+ if (retval != 0 || !phym) {
+ /* OID is optional so don't fail here. */
+ phym_unspec = RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED;
+ phym = &phym_unspec;
+ }
+ if ((flags & FLAG_RNDIS_PHYM_WIRELESS) &&
+ *phym != RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) {
+ netif_dbg(dev, probe, dev->net,
+ "driver requires wireless physical medium, but device is not\n");
+ retval = -ENODEV;
+ goto halt_fail_and_release;
+ }
+ if ((flags & FLAG_RNDIS_PHYM_NOT_WIRELESS) &&
+ *phym == RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) {
+ netif_dbg(dev, probe, dev->net,
+ "driver requires non-wireless physical medium, but device is wireless.\n");
+ retval = -ENODEV;
+ goto halt_fail_and_release;
+ }
+
+ /* Get designated host ethernet address */
+ reply_len = ETH_ALEN;
+ retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS,
+ 48, (void **) &bp, &reply_len);
+ if (unlikely(retval< 0)) {
+ dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
+ goto halt_fail_and_release;
+ }
+ memcpy(net->dev_addr, bp, ETH_ALEN);
+ memcpy(net->perm_addr, bp, ETH_ALEN);
+
+ /* set a nonzero filter to enable data transfers */
+ memset(u.set, 0, sizeof *u.set);
+ u.set->msg_type = RNDIS_MSG_SET;
+ u.set->msg_len = cpu_to_le32(4 + sizeof *u.set);
+ u.set->oid = OID_GEN_CURRENT_PACKET_FILTER;
+ u.set->len = cpu_to_le32(4);
+ u.set->offset = cpu_to_le32((sizeof *u.set) - 8);
+ *(__le32 *)(u.buf + sizeof *u.set) = RNDIS_DEFAULT_FILTER;
+
+ retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE);
+ if (unlikely(retval < 0)) {
+ dev_err(&intf->dev, "rndis set packet filter, %d\n", retval);
+ goto halt_fail_and_release;
+ }
+
+ retval = 0;
+
+ kfree(u.buf);
+ return retval;
+
+halt_fail_and_release:
+ memset(u.halt, 0, sizeof *u.halt);
+ u.halt->msg_type = RNDIS_MSG_HALT;
+ u.halt->msg_len = cpu_to_le32(sizeof *u.halt);
+ (void) rndis_command(dev, (void *)u.halt, CONTROL_BUFFER_SIZE);
+fail_and_release:
+ usb_set_intfdata(info->data, NULL);
+ usb_driver_release_interface(driver_of(intf), info->data);
+ info->data = NULL;
+fail:
+ kfree(u.buf);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(generic_rndis_bind);
+
+static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+ return generic_rndis_bind(dev, intf, FLAG_RNDIS_PHYM_NOT_WIRELESS);
+}
+
+void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+ struct rndis_halt *halt;
+
+ /* try to clear any rndis state/activity (no i/o from stack!) */
+ halt = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
+ if (halt) {
+ halt->msg_type = RNDIS_MSG_HALT;
+ halt->msg_len = cpu_to_le32(sizeof *halt);
+ (void) rndis_command(dev, (void *)halt, CONTROL_BUFFER_SIZE);
+ kfree(halt);
+ }
+
+ usbnet_cdc_unbind(dev, intf);
+}
+EXPORT_SYMBOL_GPL(rndis_unbind);
+
+/*
+ * DATA -- host must not write zlps
+ */
+int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+ /* peripheral may have batched packets to us... */
+ while (likely(skb->len)) {
+ struct rndis_data_hdr *hdr = (void *)skb->data;
+ struct sk_buff *skb2;
+ u32 msg_len, data_offset, data_len;
+
+ msg_len = le32_to_cpu(hdr->msg_len);
+ data_offset = le32_to_cpu(hdr->data_offset);
+ data_len = le32_to_cpu(hdr->data_len);
+
+ /* don't choke if we see oob, per-packet data, etc */
+ if (unlikely(hdr->msg_type != RNDIS_MSG_PACKET ||
+ skb->len < msg_len ||
+ (data_offset + data_len + 8) > msg_len)) {
+ dev->net->stats.rx_frame_errors++;
+ netdev_dbg(dev->net, "bad rndis message %d/%d/%d/%d, len %d\n",
+ le32_to_cpu(hdr->msg_type),
+ msg_len, data_offset, data_len, skb->len);
+ return 0;
+ }
+ skb_pull(skb, 8 + data_offset);
+
+ /* at most one packet left? */
+ if (likely((data_len - skb->len) <= sizeof *hdr)) {
+ skb_trim(skb, data_len);
+ break;
+ }
+
+ /* try to return all the packets in the batch */
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (unlikely(!skb2))
+ break;
+ skb_pull(skb, msg_len - sizeof *hdr);
+ skb_trim(skb2, data_len);
+ usbnet_skb_return(dev, skb2);
+ }
+
+ /* caller will usbnet_skb_return the remaining packet */
+ return 1;
+}
+EXPORT_SYMBOL_GPL(rndis_rx_fixup);
+
+struct sk_buff *
+rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
+{
+ struct rndis_data_hdr *hdr;
+ struct sk_buff *skb2;
+ unsigned len = skb->len;
+
+ if (likely(!skb_cloned(skb))) {
+ int room = skb_headroom(skb);
+
+ /* enough head room as-is? */
+ if (unlikely((sizeof *hdr) <= room))
+ goto fill;
+
+ /* enough room, but needs to be readjusted? */
+ room += skb_tailroom(skb);
+ if (likely((sizeof *hdr) <= room)) {
+ skb->data = memmove(skb->head + sizeof *hdr,
+ skb->data, len);
+ skb_set_tail_pointer(skb, len);
+ goto fill;
+ }
+ }
+
+ /* create a new skb, with the correct size (and tailpad) */
+ skb2 = skb_copy_expand(skb, sizeof *hdr, 1, flags);
+ dev_kfree_skb_any(skb);
+ if (unlikely(!skb2))
+ return skb2;
+ skb = skb2;
+
+ /* fill out the RNDIS header. we won't bother trying to batch
+ * packets; Linux minimizes wasted bandwidth through tx queues.
+ */
+fill:
+ hdr = (void *) __skb_push(skb, sizeof *hdr);
+ memset(hdr, 0, sizeof *hdr);
+ hdr->msg_type = RNDIS_MSG_PACKET;
+ hdr->msg_len = cpu_to_le32(skb->len);
+ hdr->data_offset = cpu_to_le32(sizeof(*hdr) - 8);
+ hdr->data_len = cpu_to_le32(len);
+
+ /* FIXME make the last packet always be short ... */
+ return skb;
+}
+EXPORT_SYMBOL_GPL(rndis_tx_fixup);
+
+
+static const struct driver_info rndis_info = {
+ .description = "RNDIS device",
+ .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+ .bind = rndis_bind,
+ .unbind = rndis_unbind,
+ .status = rndis_status,
+ .rx_fixup = rndis_rx_fixup,
+ .tx_fixup = rndis_tx_fixup,
+};
+
+static const struct driver_info rndis_poll_status_info = {
+ .description = "RNDIS device (poll status before control)",
+ .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+ .data = RNDIS_DRIVER_DATA_POLL_STATUS,
+ .bind = rndis_bind,
+ .unbind = rndis_unbind,
+ .status = rndis_status,
+ .rx_fixup = rndis_rx_fixup,
+ .tx_fixup = rndis_tx_fixup,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static const struct usb_device_id products [] = {
+{
+ /* 2Wire HomePortal 1000SW */
+ USB_DEVICE_AND_INTERFACE_INFO(0x1630, 0x0042,
+ USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
+ .driver_info = (unsigned long) &rndis_poll_status_info,
+}, {
+ /* RNDIS is MSFT's un-official variant of CDC ACM */
+ USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
+ .driver_info = (unsigned long) &rndis_info,
+}, {
+ /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */
+ USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1),
+ .driver_info = (unsigned long) &rndis_poll_status_info,
+}, {
+ /* RNDIS for tethering */
+ USB_INTERFACE_INFO(USB_CLASS_WIRELESS_CONTROLLER, 1, 3),
+ .driver_info = (unsigned long) &rndis_info,
+},
+ { }, // END
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver rndis_driver = {
+ .name = "rndis_host",
+ .id_table = products,
+ .probe = usbnet_probe,
+ .disconnect = usbnet_disconnect,
+ .suspend = usbnet_suspend,
+ .resume = usbnet_resume,
+};
+
+static int __init rndis_init(void)
+{
+ return usb_register(&rndis_driver);
+}
+module_init(rndis_init);
+
+static void __exit rndis_exit(void)
+{
+ usb_deregister(&rndis_driver);
+}
+module_exit(rndis_exit);
+
+MODULE_AUTHOR("David Brownell");
+MODULE_DESCRIPTION("USB Host side RNDIS driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
new file mode 100644
index 0000000..4ef7f90
--- /dev/null
+++ b/drivers/net/usb/usbnet.c
@@ -0,0 +1,1571 @@
+/*
+ * USB Network driver infrastructure
+ * Copyright (C) 2000-2005 by David Brownell
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ *
+ * 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 is a generic "USB networking" framework that works with several
+ * kinds of full and high speed networking devices: host-to-host cables,
+ * smart usb peripherals, and actual Ethernet adapters.
+ *
+ * These devices usually differ in terms of control protocols (if they
+ * even have one!) and sometimes they define new framing to wrap or batch
+ * Ethernet packets. Otherwise, they talk to USB pretty much the same,
+ * so interface (un)binding, endpoint I/O queues, fault handling, and other
+ * issues can usefully be addressed by this framework.
+ */
+
+// #define DEBUG // error path messages, extra info
+// #define VERBOSE // more; success messages
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ctype.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/usb/usbnet.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/pm_runtime.h>
+
+#define DRIVER_VERSION "22-Aug-2005"
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Nineteen USB 1.1 max size bulk transactions per frame (ms), max.
+ * Several dozen bytes of IPv4 data can fit in two such transactions.
+ * One maximum size Ethernet packet takes twenty four of them.
+ * For high speed, each frame comfortably fits almost 36 max size
+ * Ethernet packets (so queues should be bigger).
+ *
+ * REVISIT qlens should be members of 'struct usbnet'; the goal is to
+ * let the USB host controller be busy for 5msec or more before an irq
+ * is required, under load. Jumbograms change the equation.
+ */
+#define RX_MAX_QUEUE_MEMORY (60 * 1518)
+#define RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \
+ (RX_MAX_QUEUE_MEMORY/(dev)->rx_urb_size) : 4)
+#define TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \
+ (RX_MAX_QUEUE_MEMORY/(dev)->hard_mtu) : 4)
+
+// reawaken network queue this soon after stopping; else watchdog barks
+#define TX_TIMEOUT_JIFFIES (5*HZ)
+
+// throttle rx/tx briefly after some faults, so khubd might disconnect()
+// us (it polls at HZ/4 usually) before we report too many false errors.
+#define THROTTLE_JIFFIES (HZ/8)
+
+// between wakeups
+#define UNLINK_TIMEOUT_MS 3
+
+/*-------------------------------------------------------------------------*/
+
+// randomly generated ethernet address
+static u8 node_id [ETH_ALEN];
+
+static const char driver_name [] = "usbnet";
+
+/* use ethtool to change the level for any given device */
+static int msg_level = -1;
+module_param (msg_level, int, 0);
+MODULE_PARM_DESC (msg_level, "Override default message level");
+
+/*-------------------------------------------------------------------------*/
+
+/* handles CDC Ethernet and many other network "bulk data" interfaces */
+int usbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf)
+{
+ int tmp;
+ struct usb_host_interface *alt = NULL;
+ struct usb_host_endpoint *in = NULL, *out = NULL;
+ struct usb_host_endpoint *status = NULL;
+
+ for (tmp = 0; tmp < intf->num_altsetting; tmp++) {
+ unsigned ep;
+
+ in = out = status = NULL;
+ alt = intf->altsetting + tmp;
+
+ /* take the first altsetting with in-bulk + out-bulk;
+ * remember any status endpoint, just in case;
+ * ignore other endpoints and altsettings.
+ */
+ for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) {
+ struct usb_host_endpoint *e;
+ int intr = 0;
+
+ e = alt->endpoint + ep;
+ switch (e->desc.bmAttributes) {
+ case USB_ENDPOINT_XFER_INT:
+ if (!usb_endpoint_dir_in(&e->desc))
+ continue;
+ intr = 1;
+ /* FALLTHROUGH */
+ case USB_ENDPOINT_XFER_BULK:
+ break;
+ default:
+ continue;
+ }
+ if (usb_endpoint_dir_in(&e->desc)) {
+ if (!intr && !in)
+ in = e;
+ else if (intr && !status)
+ status = e;
+ } else {
+ if (!out)
+ out = e;
+ }
+ }
+ if (in && out)
+ break;
+ }
+ if (!alt || !in || !out)
+ return -EINVAL;
+
+ if (alt->desc.bAlternateSetting != 0 ||
+ !(dev->driver_info->flags & FLAG_NO_SETINT)) {
+ tmp = usb_set_interface (dev->udev, alt->desc.bInterfaceNumber,
+ alt->desc.bAlternateSetting);
+ if (tmp < 0)
+ return tmp;
+ }
+
+ dev->in = usb_rcvbulkpipe (dev->udev,
+ in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ dev->out = usb_sndbulkpipe (dev->udev,
+ out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ dev->status = status;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usbnet_get_endpoints);
+
+int usbnet_get_ethernet_addr(struct usbnet *dev, int iMACAddress)
+{
+ int tmp, i;
+ unsigned char buf [13];
+
+ tmp = usb_string(dev->udev, iMACAddress, buf, sizeof buf);
+ if (tmp != 12) {
+ dev_dbg(&dev->udev->dev,
+ "bad MAC string %d fetch, %d\n", iMACAddress, tmp);
+ if (tmp >= 0)
+ tmp = -EINVAL;
+ return tmp;
+ }
+ for (i = tmp = 0; i < 6; i++, tmp += 2)
+ dev->net->dev_addr [i] =
+ (hex_to_bin(buf[tmp]) << 4) + hex_to_bin(buf[tmp + 1]);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usbnet_get_ethernet_addr);
+
+static void intr_complete (struct urb *urb);
+
+static int init_status (struct usbnet *dev, struct usb_interface *intf)
+{
+ char *buf = NULL;
+ unsigned pipe = 0;
+ unsigned maxp;
+ unsigned period;
+
+ if (!dev->driver_info->status)
+ return 0;
+
+ pipe = usb_rcvintpipe (dev->udev,
+ dev->status->desc.bEndpointAddress
+ & USB_ENDPOINT_NUMBER_MASK);
+ maxp = usb_maxpacket (dev->udev, pipe, 0);
+
+ /* avoid 1 msec chatter: min 8 msec poll rate */
+ period = max ((int) dev->status->desc.bInterval,
+ (dev->udev->speed == USB_SPEED_HIGH) ? 7 : 3);
+
+ buf = kmalloc (maxp, GFP_KERNEL);
+ if (buf) {
+ dev->interrupt = usb_alloc_urb (0, GFP_KERNEL);
+ if (!dev->interrupt) {
+ kfree (buf);
+ return -ENOMEM;
+ } else {
+ usb_fill_int_urb(dev->interrupt, dev->udev, pipe,
+ buf, maxp, intr_complete, dev, period);
+ dev_dbg(&intf->dev,
+ "status ep%din, %d bytes period %d\n",
+ usb_pipeendpoint(pipe), maxp, period);
+ }
+ }
+ return 0;
+}
+
+/* Passes this packet up the stack, updating its accounting.
+ * Some link protocols batch packets, so their rx_fixup paths
+ * can return clones as well as just modify the original skb.
+ */
+void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
+{
+ int status;
+
+ if (test_bit(EVENT_RX_PAUSED, &dev->flags)) {
+ skb_queue_tail(&dev->rxq_pause, skb);
+ return;
+ }
+
+ skb->protocol = eth_type_trans (skb, dev->net);
+ dev->net->stats.rx_packets++;
+ dev->net->stats.rx_bytes += skb->len;
+
+ netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n",
+ skb->len + sizeof (struct ethhdr), skb->protocol);
+ memset (skb->cb, 0, sizeof (struct skb_data));
+
+ if (skb_defer_rx_timestamp(skb))
+ return;
+
+ status = netif_rx (skb);
+ if (status != NET_RX_SUCCESS)
+ netif_dbg(dev, rx_err, dev->net,
+ "netif_rx status %d\n", status);
+}
+EXPORT_SYMBOL_GPL(usbnet_skb_return);
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Network Device Driver (peer link to "Host Device", from USB host)
+ *
+ *-------------------------------------------------------------------------*/
+
+int usbnet_change_mtu (struct net_device *net, int new_mtu)
+{
+ struct usbnet *dev = netdev_priv(net);
+ int ll_mtu = new_mtu + net->hard_header_len;
+ int old_hard_mtu = dev->hard_mtu;
+ int old_rx_urb_size = dev->rx_urb_size;
+
+ if (new_mtu <= 0)
+ return -EINVAL;
+ // no second zero-length packet read wanted after mtu-sized packets
+ if ((ll_mtu % dev->maxpacket) == 0)
+ return -EDOM;
+ net->mtu = new_mtu;
+
+ dev->hard_mtu = net->mtu + net->hard_header_len;
+ if (dev->rx_urb_size == old_hard_mtu) {
+ dev->rx_urb_size = dev->hard_mtu;
+ if (dev->rx_urb_size > old_rx_urb_size)
+ usbnet_unlink_rx_urbs(dev);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usbnet_change_mtu);
+
+/*-------------------------------------------------------------------------*/
+
+/* some LK 2.4 HCDs oopsed if we freed or resubmitted urbs from
+ * completion callbacks. 2.5 should have fixed those bugs...
+ */
+
+static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_head *list)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&list->lock, flags);
+ __skb_unlink(skb, list);
+ spin_unlock(&list->lock);
+ spin_lock(&dev->done.lock);
+ __skb_queue_tail(&dev->done, skb);
+ if (dev->done.qlen == 1)
+ tasklet_schedule(&dev->bh);
+ spin_unlock_irqrestore(&dev->done.lock, flags);
+}
+
+/* some work can't be done in tasklets, so we use keventd
+ *
+ * NOTE: annoying asymmetry: if it's active, schedule_work() fails,
+ * but tasklet_schedule() doesn't. hope the failure is rare.
+ */
+void usbnet_defer_kevent (struct usbnet *dev, int work)
+{
+ set_bit (work, &dev->flags);
+ if (!schedule_work (&dev->kevent))
+ netdev_err(dev->net, "kevent %d may have been dropped\n", work);
+ else
+ netdev_dbg(dev->net, "kevent %d scheduled\n", work);
+}
+EXPORT_SYMBOL_GPL(usbnet_defer_kevent);
+
+/*-------------------------------------------------------------------------*/
+
+static void rx_complete (struct urb *urb);
+
+static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
+{
+ struct sk_buff *skb;
+ struct skb_data *entry;
+ int retval = 0;
+ unsigned long lockflags;
+ size_t size = dev->rx_urb_size;
+
+ if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) {
+ netif_dbg(dev, rx_err, dev->net, "no rx skb\n");
+ usbnet_defer_kevent (dev, EVENT_RX_MEMORY);
+ usb_free_urb (urb);
+ return -ENOMEM;
+ }
+ skb_reserve (skb, NET_IP_ALIGN);
+
+ entry = (struct skb_data *) skb->cb;
+ entry->urb = urb;
+ entry->dev = dev;
+ entry->state = rx_start;
+ entry->length = 0;
+
+ usb_fill_bulk_urb (urb, dev->udev, dev->in,
+ skb->data, size, rx_complete, skb);
+
+ spin_lock_irqsave (&dev->rxq.lock, lockflags);
+
+ if (netif_running (dev->net) &&
+ netif_device_present (dev->net) &&
+ !test_bit (EVENT_RX_HALT, &dev->flags) &&
+ !test_bit (EVENT_DEV_ASLEEP, &dev->flags)) {
+ switch (retval = usb_submit_urb (urb, GFP_ATOMIC)) {
+ case -EPIPE:
+ usbnet_defer_kevent (dev, EVENT_RX_HALT);
+ break;
+ case -ENOMEM:
+ usbnet_defer_kevent (dev, EVENT_RX_MEMORY);
+ break;
+ case -ENODEV:
+ netif_dbg(dev, ifdown, dev->net, "device gone\n");
+ netif_device_detach (dev->net);
+ break;
+ case -EHOSTUNREACH:
+ retval = -ENOLINK;
+ break;
+ default:
+ netif_dbg(dev, rx_err, dev->net,
+ "rx submit, %d\n", retval);
+ tasklet_schedule (&dev->bh);
+ break;
+ case 0:
+ __skb_queue_tail (&dev->rxq, skb);
+ }
+ } else {
+ netif_dbg(dev, ifdown, dev->net, "rx: stopped\n");
+ retval = -ENOLINK;
+ }
+ spin_unlock_irqrestore (&dev->rxq.lock, lockflags);
+ if (retval) {
+ dev_kfree_skb_any (skb);
+ usb_free_urb (urb);
+ }
+ return retval;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
+{
+ if (dev->driver_info->rx_fixup &&
+ !dev->driver_info->rx_fixup (dev, skb)) {
+ /* With RX_ASSEMBLE, rx_fixup() must update counters */
+ if (!(dev->driver_info->flags & FLAG_RX_ASSEMBLE))
+ dev->net->stats.rx_errors++;
+ goto done;
+ }
+ // else network stack removes extra byte if we forced a short packet
+
+ if (skb->len) {
+ /* all data was already cloned from skb inside the driver */
+ if (dev->driver_info->flags & FLAG_MULTI_PACKET)
+ dev_kfree_skb_any(skb);
+ else
+ usbnet_skb_return(dev, skb);
+ return;
+ }
+
+ netif_dbg(dev, rx_err, dev->net, "drop\n");
+ dev->net->stats.rx_errors++;
+done:
+ skb_queue_tail(&dev->done, skb);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void rx_complete (struct urb *urb)
+{
+ struct sk_buff *skb = (struct sk_buff *) urb->context;
+ struct skb_data *entry = (struct skb_data *) skb->cb;
+ struct usbnet *dev = entry->dev;
+ int urb_status = urb->status;
+
+ skb_put (skb, urb->actual_length);
+ entry->state = rx_done;
+ entry->urb = NULL;
+
+ switch (urb_status) {
+ /* success */
+ case 0:
+ if (skb->len < dev->net->hard_header_len) {
+ entry->state = rx_cleanup;
+ dev->net->stats.rx_errors++;
+ dev->net->stats.rx_length_errors++;
+ netif_dbg(dev, rx_err, dev->net,
+ "rx length %d\n", skb->len);
+ }
+ break;
+
+ /* stalls need manual reset. this is rare ... except that
+ * when going through USB 2.0 TTs, unplug appears this way.
+ * we avoid the highspeed version of the ETIMEDOUT/EILSEQ
+ * storm, recovering as needed.
+ */
+ case -EPIPE:
+ dev->net->stats.rx_errors++;
+ usbnet_defer_kevent (dev, EVENT_RX_HALT);
+ // FALLTHROUGH
+
+ /* software-driven interface shutdown */
+ case -ECONNRESET: /* async unlink */
+ case -ESHUTDOWN: /* hardware gone */
+ netif_dbg(dev, ifdown, dev->net,
+ "rx shutdown, code %d\n", urb_status);
+ goto block;
+
+ /* we get controller i/o faults during khubd disconnect() delays.
+ * throttle down resubmits, to avoid log floods; just temporarily,
+ * so we still recover when the fault isn't a khubd delay.
+ */
+ case -EPROTO:
+ case -ETIME:
+ case -EILSEQ:
+ dev->net->stats.rx_errors++;
+ if (!timer_pending (&dev->delay)) {
+ mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES);
+ netif_dbg(dev, link, dev->net,
+ "rx throttle %d\n", urb_status);
+ }
+block:
+ entry->state = rx_cleanup;
+ entry->urb = urb;
+ urb = NULL;
+ break;
+
+ /* data overrun ... flush fifo? */
+ case -EOVERFLOW:
+ dev->net->stats.rx_over_errors++;
+ // FALLTHROUGH
+
+ default:
+ entry->state = rx_cleanup;
+ dev->net->stats.rx_errors++;
+ netif_dbg(dev, rx_err, dev->net, "rx status %d\n", urb_status);
+ break;
+ }
+
+ defer_bh(dev, skb, &dev->rxq);
+
+ if (urb) {
+ if (netif_running (dev->net) &&
+ !test_bit (EVENT_RX_HALT, &dev->flags)) {
+ rx_submit (dev, urb, GFP_ATOMIC);
+ return;
+ }
+ usb_free_urb (urb);
+ }
+ 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)
+{
+ set_bit(EVENT_RX_PAUSED, &dev->flags);
+
+ netif_dbg(dev, rx_status, dev->net, "paused rx queue enabled\n");
+}
+EXPORT_SYMBOL_GPL(usbnet_pause_rx);
+
+void usbnet_resume_rx(struct usbnet *dev)
+{
+ struct sk_buff *skb;
+ int num = 0;
+
+ clear_bit(EVENT_RX_PAUSED, &dev->flags);
+
+ while ((skb = skb_dequeue(&dev->rxq_pause)) != NULL) {
+ usbnet_skb_return(dev, skb);
+ num++;
+ }
+
+ tasklet_schedule(&dev->bh);
+
+ netif_dbg(dev, rx_status, dev->net,
+ "paused rx queue disabled, %d skbs requeued\n", num);
+}
+EXPORT_SYMBOL_GPL(usbnet_resume_rx);
+
+void usbnet_purge_paused_rxq(struct usbnet *dev)
+{
+ skb_queue_purge(&dev->rxq_pause);
+}
+EXPORT_SYMBOL_GPL(usbnet_purge_paused_rxq);
+
+/*-------------------------------------------------------------------------*/
+
+// unlink pending rx/tx; completion handlers do all other cleanup
+
+static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
+{
+ unsigned long flags;
+ struct sk_buff *skb, *skbnext;
+ int count = 0;
+
+ spin_lock_irqsave (&q->lock, flags);
+ skb_queue_walk_safe(q, skb, skbnext) {
+ struct skb_data *entry;
+ struct urb *urb;
+ int retval;
+
+ entry = (struct skb_data *) skb->cb;
+ urb = entry->urb;
+
+ // during some PM-driven resume scenarios,
+ // these (async) unlinks complete immediately
+ retval = usb_unlink_urb (urb);
+ if (retval != -EINPROGRESS && retval != 0)
+ netdev_dbg(dev->net, "unlink urb err, %d\n", retval);
+ else
+ count++;
+ }
+ spin_unlock_irqrestore (&q->lock, flags);
+ return count;
+}
+
+// Flush all pending rx urbs
+// minidrivers may need to do this when the MTU changes
+
+void usbnet_unlink_rx_urbs(struct usbnet *dev)
+{
+ if (netif_running(dev->net)) {
+ (void) unlink_urbs (dev, &dev->rxq);
+ tasklet_schedule(&dev->bh);
+ }
+}
+EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs);
+
+/*-------------------------------------------------------------------------*/
+
+// precondition: never called in_interrupt
+static void usbnet_terminate_urbs(struct usbnet *dev)
+{
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(unlink_wakeup);
+ DECLARE_WAITQUEUE(wait, current);
+ int temp;
+
+ /* ensure there are no more active urbs */
+ add_wait_queue(&unlink_wakeup, &wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ dev->wait = &unlink_wakeup;
+ temp = unlink_urbs(dev, &dev->txq) +
+ unlink_urbs(dev, &dev->rxq);
+
+ /* maybe wait for deletions to finish. */
+ while (!skb_queue_empty(&dev->rxq)
+ && !skb_queue_empty(&dev->txq)
+ && !skb_queue_empty(&dev->done)) {
+ schedule_timeout(msecs_to_jiffies(UNLINK_TIMEOUT_MS));
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ netif_dbg(dev, ifdown, dev->net,
+ "waited for %d urb completions\n", temp);
+ }
+ set_current_state(TASK_RUNNING);
+ dev->wait = NULL;
+ remove_wait_queue(&unlink_wakeup, &wait);
+}
+
+int usbnet_stop (struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+ struct driver_info *info = dev->driver_info;
+ int retval;
+
+ clear_bit(EVENT_DEV_OPEN, &dev->flags);
+ netif_stop_queue (net);
+
+ netif_info(dev, ifdown, dev->net,
+ "stop stats: rx/tx %lu/%lu, errs %lu/%lu\n",
+ net->stats.rx_packets, net->stats.tx_packets,
+ net->stats.rx_errors, net->stats.tx_errors);
+
+ /* allow minidriver to stop correctly (wireless devices to turn off
+ * radio etc) */
+ if (info->stop) {
+ retval = info->stop(dev);
+ if (retval < 0)
+ netif_info(dev, ifdown, dev->net,
+ "stop fail (%d) usbnet usb-%s-%s, %s\n",
+ retval,
+ dev->udev->bus->bus_name, dev->udev->devpath,
+ info->description);
+ }
+
+ if (!(info->flags & FLAG_AVOID_UNLINK_URBS))
+ usbnet_terminate_urbs(dev);
+
+ usb_kill_urb(dev->interrupt);
+
+ usbnet_purge_paused_rxq(dev);
+
+ /* deferred work (task, timer, softirq) must also stop.
+ * can't flush_scheduled_work() until we drop rtnl (later),
+ * else workers could deadlock; so make workers a NOP.
+ */
+ dev->flags = 0;
+ del_timer_sync (&dev->delay);
+ tasklet_kill (&dev->bh);
+ if (info->manage_power)
+ info->manage_power(dev, 0);
+ else
+ usb_autopm_put_interface(dev->intf);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usbnet_stop);
+
+/*-------------------------------------------------------------------------*/
+
+// posts reads, and enables write queuing
+
+// precondition: never called in_interrupt
+
+int usbnet_open (struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+ int retval;
+ struct driver_info *info = dev->driver_info;
+
+ if ((retval = usb_autopm_get_interface(dev->intf)) < 0) {
+ netif_info(dev, ifup, dev->net,
+ "resumption fail (%d) usbnet usb-%s-%s, %s\n",
+ retval,
+ dev->udev->bus->bus_name,
+ dev->udev->devpath,
+ info->description);
+ goto done_nopm;
+ }
+
+ // put into "known safe" state
+ if (info->reset && (retval = info->reset (dev)) < 0) {
+ netif_info(dev, ifup, dev->net,
+ "open reset fail (%d) usbnet usb-%s-%s, %s\n",
+ retval,
+ dev->udev->bus->bus_name,
+ dev->udev->devpath,
+ info->description);
+ goto done;
+ }
+
+ // insist peer be connected
+ if (info->check_connect && (retval = info->check_connect (dev)) < 0) {
+ netif_dbg(dev, ifup, dev->net, "can't open; %d\n", retval);
+ goto done;
+ }
+
+ /* start any status interrupt transfer */
+ if (dev->interrupt) {
+ retval = usb_submit_urb (dev->interrupt, GFP_KERNEL);
+ if (retval < 0) {
+ netif_err(dev, ifup, dev->net,
+ "intr submit %d\n", retval);
+ goto done;
+ }
+ }
+
+ set_bit(EVENT_DEV_OPEN, &dev->flags);
+ netif_start_queue (net);
+ netif_info(dev, ifup, dev->net,
+ "open: enable queueing (rx %d, tx %d) mtu %d %s framing\n",
+ (int)RX_QLEN(dev), (int)TX_QLEN(dev),
+ dev->net->mtu,
+ (dev->driver_info->flags & FLAG_FRAMING_NC) ? "NetChip" :
+ (dev->driver_info->flags & FLAG_FRAMING_GL) ? "GeneSys" :
+ (dev->driver_info->flags & FLAG_FRAMING_Z) ? "Zaurus" :
+ (dev->driver_info->flags & FLAG_FRAMING_RN) ? "RNDIS" :
+ (dev->driver_info->flags & FLAG_FRAMING_AX) ? "ASIX" :
+ "simple");
+
+ // delay posting reads until we're fully open
+ tasklet_schedule (&dev->bh);
+ if (info->manage_power) {
+ retval = info->manage_power(dev, 1);
+ if (retval < 0)
+ goto done;
+ usb_autopm_put_interface(dev->intf);
+ }
+ return retval;
+
+done:
+ usb_autopm_put_interface(dev->intf);
+done_nopm:
+ return retval;
+}
+EXPORT_SYMBOL_GPL(usbnet_open);
+
+/*-------------------------------------------------------------------------*/
+
+/* ethtool methods; minidrivers may need to add some more, but
+ * they'll probably want to use this base set.
+ */
+
+int usbnet_get_settings (struct net_device *net, struct ethtool_cmd *cmd)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ if (!dev->mii.mdio_read)
+ return -EOPNOTSUPP;
+
+ return mii_ethtool_gset(&dev->mii, cmd);
+}
+EXPORT_SYMBOL_GPL(usbnet_get_settings);
+
+int usbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd)
+{
+ struct usbnet *dev = netdev_priv(net);
+ int retval;
+
+ if (!dev->mii.mdio_write)
+ return -EOPNOTSUPP;
+
+ retval = mii_ethtool_sset(&dev->mii, cmd);
+
+ /* link speed/duplex might have changed */
+ if (dev->driver_info->link_reset)
+ dev->driver_info->link_reset(dev);
+
+ return retval;
+
+}
+EXPORT_SYMBOL_GPL(usbnet_set_settings);
+
+u32 usbnet_get_link (struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ /* If a check_connect is defined, return its result */
+ if (dev->driver_info->check_connect)
+ return dev->driver_info->check_connect (dev) == 0;
+
+ /* if the device has mii operations, use those */
+ if (dev->mii.mdio_read)
+ return mii_link_ok(&dev->mii);
+
+ /* Otherwise, dtrt for drivers calling netif_carrier_{on,off} */
+ return ethtool_op_get_link(net);
+}
+EXPORT_SYMBOL_GPL(usbnet_get_link);
+
+int usbnet_nway_reset(struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ if (!dev->mii.mdio_write)
+ return -EOPNOTSUPP;
+
+ return mii_nway_restart(&dev->mii);
+}
+EXPORT_SYMBOL_GPL(usbnet_nway_reset);
+
+void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ strncpy (info->driver, dev->driver_name, sizeof info->driver);
+ strncpy (info->version, DRIVER_VERSION, sizeof info->version);
+ strncpy (info->fw_version, dev->driver_info->description,
+ sizeof info->fw_version);
+ usb_make_path (dev->udev, info->bus_info, sizeof info->bus_info);
+}
+EXPORT_SYMBOL_GPL(usbnet_get_drvinfo);
+
+u32 usbnet_get_msglevel (struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ return dev->msg_enable;
+}
+EXPORT_SYMBOL_GPL(usbnet_get_msglevel);
+
+void usbnet_set_msglevel (struct net_device *net, u32 level)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ dev->msg_enable = level;
+}
+EXPORT_SYMBOL_GPL(usbnet_set_msglevel);
+
+/* drivers may override default ethtool_ops in their bind() routine */
+static const struct ethtool_ops usbnet_ethtool_ops = {
+ .get_settings = usbnet_get_settings,
+ .set_settings = usbnet_set_settings,
+ .get_link = usbnet_get_link,
+ .nway_reset = usbnet_nway_reset,
+ .get_drvinfo = usbnet_get_drvinfo,
+ .get_msglevel = usbnet_get_msglevel,
+ .set_msglevel = usbnet_set_msglevel,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* work that cannot be done in interrupt context uses keventd.
+ *
+ * NOTE: with 2.5 we could do more of this using completion callbacks,
+ * especially now that control transfers can be queued.
+ */
+static void
+kevent (struct work_struct *work)
+{
+ struct usbnet *dev =
+ container_of(work, struct usbnet, kevent);
+ int status;
+
+ /* usb_clear_halt() needs a thread context */
+ if (test_bit (EVENT_TX_HALT, &dev->flags)) {
+ unlink_urbs (dev, &dev->txq);
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0)
+ goto fail_pipe;
+ status = usb_clear_halt (dev->udev, dev->out);
+ usb_autopm_put_interface(dev->intf);
+ if (status < 0 &&
+ status != -EPIPE &&
+ status != -ESHUTDOWN) {
+ if (netif_msg_tx_err (dev))
+fail_pipe:
+ netdev_err(dev->net, "can't clear tx halt, status %d\n",
+ status);
+ } else {
+ clear_bit (EVENT_TX_HALT, &dev->flags);
+ if (status != -ESHUTDOWN)
+ netif_wake_queue (dev->net);
+ }
+ }
+ if (test_bit (EVENT_RX_HALT, &dev->flags)) {
+ unlink_urbs (dev, &dev->rxq);
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0)
+ goto fail_halt;
+ status = usb_clear_halt (dev->udev, dev->in);
+ usb_autopm_put_interface(dev->intf);
+ if (status < 0 &&
+ status != -EPIPE &&
+ status != -ESHUTDOWN) {
+ if (netif_msg_rx_err (dev))
+fail_halt:
+ netdev_err(dev->net, "can't clear rx halt, status %d\n",
+ status);
+ } else {
+ clear_bit (EVENT_RX_HALT, &dev->flags);
+ tasklet_schedule (&dev->bh);
+ }
+ }
+
+ /* tasklet could resubmit itself forever if memory is tight */
+ if (test_bit (EVENT_RX_MEMORY, &dev->flags)) {
+ struct urb *urb = NULL;
+ int resched = 1;
+
+ if (netif_running (dev->net))
+ urb = usb_alloc_urb (0, GFP_KERNEL);
+ else
+ clear_bit (EVENT_RX_MEMORY, &dev->flags);
+ if (urb != NULL) {
+ clear_bit (EVENT_RX_MEMORY, &dev->flags);
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0) {
+ usb_free_urb(urb);
+ goto fail_lowmem;
+ }
+ if (rx_submit (dev, urb, GFP_KERNEL) == -ENOLINK)
+ resched = 0;
+ usb_autopm_put_interface(dev->intf);
+fail_lowmem:
+ if (resched)
+ tasklet_schedule (&dev->bh);
+ }
+ }
+
+ if (test_bit (EVENT_LINK_RESET, &dev->flags)) {
+ struct driver_info *info = dev->driver_info;
+ int retval = 0;
+
+ clear_bit (EVENT_LINK_RESET, &dev->flags);
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0)
+ goto skip_reset;
+ if(info->link_reset && (retval = info->link_reset(dev)) < 0) {
+ usb_autopm_put_interface(dev->intf);
+skip_reset:
+ netdev_info(dev->net, "link reset failed (%d) usbnet usb-%s-%s, %s\n",
+ retval,
+ dev->udev->bus->bus_name,
+ dev->udev->devpath,
+ info->description);
+ } else {
+ usb_autopm_put_interface(dev->intf);
+ }
+ }
+
+ if (dev->flags)
+ netdev_dbg(dev->net, "kevent done, flags = 0x%lx\n", dev->flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void tx_complete (struct urb *urb)
+{
+ struct sk_buff *skb = (struct sk_buff *) urb->context;
+ struct skb_data *entry = (struct skb_data *) skb->cb;
+ struct usbnet *dev = entry->dev;
+
+ if (urb->status == 0) {
+ if (!(dev->driver_info->flags & FLAG_MULTI_PACKET))
+ dev->net->stats.tx_packets++;
+ dev->net->stats.tx_bytes += entry->length;
+ } else {
+ dev->net->stats.tx_errors++;
+
+ switch (urb->status) {
+ case -EPIPE:
+ usbnet_defer_kevent (dev, EVENT_TX_HALT);
+ break;
+
+ /* software-driven interface shutdown */
+ case -ECONNRESET: // async unlink
+ case -ESHUTDOWN: // hardware gone
+ break;
+
+ // like rx, tx gets controller i/o faults during khubd delays
+ // and so it uses the same throttling mechanism.
+ case -EPROTO:
+ case -ETIME:
+ case -EILSEQ:
+ usb_mark_last_busy(dev->udev);
+ if (!timer_pending (&dev->delay)) {
+ mod_timer (&dev->delay,
+ jiffies + THROTTLE_JIFFIES);
+ netif_dbg(dev, link, dev->net,
+ "tx throttle %d\n", urb->status);
+ }
+ netif_stop_queue (dev->net);
+ break;
+ default:
+ netif_dbg(dev, tx_err, dev->net,
+ "tx err %d\n", entry->urb->status);
+ break;
+ }
+ }
+
+ usb_autopm_put_interface_async(dev->intf);
+ urb->dev = NULL;
+ entry->state = tx_done;
+ defer_bh(dev, skb, &dev->txq);
+}
+
+/*-------------------------------------------------------------------------*/
+
+void usbnet_tx_timeout (struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ unlink_urbs (dev, &dev->txq);
+ tasklet_schedule (&dev->bh);
+
+ // FIXME: device recovery -- reset?
+}
+EXPORT_SYMBOL_GPL(usbnet_tx_timeout);
+
+/*-------------------------------------------------------------------------*/
+
+netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
+ struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+ int length;
+ struct urb *urb = NULL;
+ struct skb_data *entry;
+ struct driver_info *info = dev->driver_info;
+ unsigned long flags;
+ int retval;
+
+ if (skb)
+ skb_tx_timestamp(skb);
+
+ // some devices want funky USB-level framing, for
+ // win32 driver (usually) and/or hardware quirks
+ if (info->tx_fixup) {
+ skb = info->tx_fixup (dev, skb, GFP_ATOMIC);
+ if (!skb) {
+ if (netif_msg_tx_err(dev)) {
+ netif_dbg(dev, tx_err, dev->net, "can't tx_fixup skb\n");
+ goto drop;
+ } else {
+ /* cdc_ncm collected packet; waits for more */
+ goto not_drop;
+ }
+ }
+ }
+ length = skb->len;
+
+ if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) {
+ netif_dbg(dev, tx_err, dev->net, "no urb\n");
+ goto drop;
+ }
+
+ entry = (struct skb_data *) skb->cb;
+ entry->urb = urb;
+ entry->dev = dev;
+ entry->state = tx_start;
+ entry->length = length;
+
+ usb_fill_bulk_urb (urb, dev->udev, dev->out,
+ skb->data, skb->len, tx_complete, skb);
+
+ /* don't assume the hardware handles USB_ZERO_PACKET
+ * NOTE: strictly conforming cdc-ether devices should expect
+ * the ZLP here, but ignore the one-byte packet.
+ * NOTE2: CDC NCM specification is different from CDC ECM when
+ * handling ZLP/short packets, so cdc_ncm driver will make short
+ * packet itself if needed.
+ */
+ if (length % dev->maxpacket == 0) {
+ if (!(info->flags & FLAG_SEND_ZLP)) {
+ if (!(info->flags & FLAG_MULTI_PACKET)) {
+ urb->transfer_buffer_length++;
+ if (skb_tailroom(skb)) {
+ skb->data[skb->len] = 0;
+ __skb_put(skb, 1);
+ }
+ }
+ } else
+ urb->transfer_flags |= URB_ZERO_PACKET;
+ }
+
+ spin_lock_irqsave(&dev->txq.lock, flags);
+ retval = usb_autopm_get_interface_async(dev->intf);
+ if (retval < 0) {
+ spin_unlock_irqrestore(&dev->txq.lock, flags);
+ goto drop;
+ }
+
+#ifdef CONFIG_PM
+ /* if this triggers the device is still a sleep */
+ if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) {
+ /* transmission will be done in resume */
+ usb_anchor_urb(urb, &dev->deferred);
+ /* no use to process more packets */
+ netif_stop_queue(net);
+ spin_unlock_irqrestore(&dev->txq.lock, flags);
+ netdev_dbg(dev->net, "Delaying transmission for resumption\n");
+ goto deferred;
+ }
+#endif
+
+ switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) {
+ case -EPIPE:
+ netif_stop_queue (net);
+ usbnet_defer_kevent (dev, EVENT_TX_HALT);
+ usb_autopm_put_interface_async(dev->intf);
+ break;
+ default:
+ usb_autopm_put_interface_async(dev->intf);
+ netif_dbg(dev, tx_err, dev->net,
+ "tx: submit urb err %d\n", retval);
+ break;
+ case 0:
+ net->trans_start = jiffies;
+ __skb_queue_tail (&dev->txq, skb);
+ if (dev->txq.qlen >= TX_QLEN (dev))
+ netif_stop_queue (net);
+ }
+ spin_unlock_irqrestore (&dev->txq.lock, flags);
+
+ if (retval) {
+ netif_dbg(dev, tx_err, dev->net, "drop, code %d\n", retval);
+drop:
+ dev->net->stats.tx_dropped++;
+not_drop:
+ if (skb)
+ dev_kfree_skb_any (skb);
+ usb_free_urb (urb);
+ } else
+ netif_dbg(dev, tx_queued, dev->net,
+ "> tx, len %d, type 0x%x\n", length, skb->protocol);
+#ifdef CONFIG_PM
+deferred:
+#endif
+ return NETDEV_TX_OK;
+}
+EXPORT_SYMBOL_GPL(usbnet_start_xmit);
+
+/*-------------------------------------------------------------------------*/
+
+// tasklet (work deferred from completions, in_irq) or timer
+
+static void usbnet_bh (unsigned long param)
+{
+ struct usbnet *dev = (struct usbnet *) param;
+ struct sk_buff *skb;
+ struct skb_data *entry;
+
+ while ((skb = skb_dequeue (&dev->done))) {
+ entry = (struct skb_data *) skb->cb;
+ switch (entry->state) {
+ case rx_done:
+ entry->state = rx_cleanup;
+ rx_process (dev, skb);
+ continue;
+ case tx_done:
+ case rx_cleanup:
+ usb_free_urb (entry->urb);
+ dev_kfree_skb (skb);
+ continue;
+ default:
+ netdev_dbg(dev->net, "bogus skb state %d\n", entry->state);
+ }
+ }
+
+ // waiting for all pending urbs to complete?
+ if (dev->wait) {
+ if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) {
+ wake_up (dev->wait);
+ }
+
+ // or are we maybe short a few urbs?
+ } else if (netif_running (dev->net) &&
+ netif_device_present (dev->net) &&
+ !timer_pending (&dev->delay) &&
+ !test_bit (EVENT_RX_HALT, &dev->flags)) {
+ int temp = dev->rxq.qlen;
+ int qlen = RX_QLEN (dev);
+
+ if (temp < qlen) {
+ struct urb *urb;
+ int i;
+
+ // don't refill the queue all at once
+ for (i = 0; i < 10 && dev->rxq.qlen < qlen; i++) {
+ urb = usb_alloc_urb (0, GFP_ATOMIC);
+ if (urb != NULL) {
+ if (rx_submit (dev, urb, GFP_ATOMIC) ==
+ -ENOLINK)
+ return;
+ }
+ }
+ if (temp != dev->rxq.qlen)
+ netif_dbg(dev, link, dev->net,
+ "rxqlen %d --> %d\n",
+ temp, dev->rxq.qlen);
+ if (dev->rxq.qlen < qlen)
+ tasklet_schedule (&dev->bh);
+ }
+ if (dev->txq.qlen < TX_QLEN (dev))
+ netif_wake_queue (dev->net);
+ }
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * USB Device Driver support
+ *
+ *-------------------------------------------------------------------------*/
+
+// precondition: never called in_interrupt
+
+void usbnet_disconnect (struct usb_interface *intf)
+{
+ struct usbnet *dev;
+ struct usb_device *xdev;
+ struct net_device *net;
+
+ dev = usb_get_intfdata(intf);
+ usb_set_intfdata(intf, NULL);
+ if (!dev)
+ return;
+
+ xdev = interface_to_usbdev (intf);
+
+ netif_info(dev, probe, dev->net, "unregister '%s' usb-%s-%s, %s\n",
+ intf->dev.driver->name,
+ xdev->bus->bus_name, xdev->devpath,
+ dev->driver_info->description);
+
+ net = dev->net;
+ unregister_netdev (net);
+
+ cancel_work_sync(&dev->kevent);
+
+ if (dev->driver_info->unbind)
+ dev->driver_info->unbind (dev, intf);
+
+ usb_kill_urb(dev->interrupt);
+ usb_free_urb(dev->interrupt);
+
+ free_netdev(net);
+ usb_put_dev (xdev);
+}
+EXPORT_SYMBOL_GPL(usbnet_disconnect);
+
+static const struct net_device_ops usbnet_netdev_ops = {
+ .ndo_open = usbnet_open,
+ .ndo_stop = usbnet_stop,
+ .ndo_start_xmit = usbnet_start_xmit,
+ .ndo_tx_timeout = usbnet_tx_timeout,
+ .ndo_change_mtu = usbnet_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+/*-------------------------------------------------------------------------*/
+
+// precondition: never called in_interrupt
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
+static struct device_type wlan_type = {
+ .name = "wlan",
+};
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
+static struct device_type wwan_type = {
+ .name = "wwan",
+};
+#endif
+
+int
+usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
+{
+ struct usbnet *dev;
+ struct net_device *net;
+ struct usb_host_interface *interface;
+ struct driver_info *info;
+ struct usb_device *xdev;
+ int status;
+ const char *name;
+ struct usb_driver *driver = to_usb_driver(udev->dev.driver);
+
+ /* usbnet already took usb runtime pm, so have to enable the feature
+ * for usb interface, otherwise usb_autopm_get_interface may return
+ * failure if USB_SUSPEND(RUNTIME_PM) is enabled.
+ */
+ if (!driver->supports_autosuspend) {
+ driver->supports_autosuspend = 1;
+ pm_runtime_enable(&udev->dev);
+ }
+
+ name = udev->dev.driver->name;
+ info = (struct driver_info *) prod->driver_info;
+ if (!info) {
+ dev_dbg (&udev->dev, "blacklisted by %s\n", name);
+ return -ENODEV;
+ }
+ xdev = interface_to_usbdev (udev);
+ interface = udev->cur_altsetting;
+
+ usb_get_dev (xdev);
+
+ status = -ENOMEM;
+
+ // set up our own records
+ net = alloc_etherdev(sizeof(*dev));
+ if (!net) {
+ dbg ("can't kmalloc dev");
+ goto out;
+ }
+
+ /* netdev_printk() needs this so do it as early as possible */
+ SET_NETDEV_DEV(net, &udev->dev);
+
+ dev = netdev_priv(net);
+ dev->udev = xdev;
+ dev->intf = udev;
+ dev->driver_info = info;
+ dev->driver_name = name;
+ dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV
+ | NETIF_MSG_PROBE | NETIF_MSG_LINK);
+ skb_queue_head_init (&dev->rxq);
+ skb_queue_head_init (&dev->txq);
+ skb_queue_head_init (&dev->done);
+ skb_queue_head_init(&dev->rxq_pause);
+ dev->bh.func = usbnet_bh;
+ dev->bh.data = (unsigned long) dev;
+ INIT_WORK (&dev->kevent, kevent);
+ init_usb_anchor(&dev->deferred);
+ dev->delay.function = usbnet_bh;
+ dev->delay.data = (unsigned long) dev;
+ init_timer (&dev->delay);
+ mutex_init (&dev->phy_mutex);
+
+ dev->net = net;
+ strcpy (net->name, "usb%d");
+ memcpy (net->dev_addr, node_id, sizeof node_id);
+
+ /* rx and tx sides can use different message sizes;
+ * bind() should set rx_urb_size in that case.
+ */
+ dev->hard_mtu = net->mtu + net->hard_header_len;
+#if 0
+// dma_supported() is deeply broken on almost all architectures
+ // possible with some EHCI controllers
+ if (dma_supported (&udev->dev, DMA_BIT_MASK(64)))
+ net->features |= NETIF_F_HIGHDMA;
+#endif
+
+ netdev_attach_ops(net, &usbnet_netdev_ops);
+ net->watchdog_timeo = TX_TIMEOUT_JIFFIES;
+ net->ethtool_ops = &usbnet_ethtool_ops;
+
+ // allow device-specific bind/init procedures
+ // NOTE net->name still not usable ...
+ if (info->bind) {
+ status = info->bind (dev, udev);
+ if (status < 0)
+ goto out1;
+
+ // heuristic: "usb%d" for links we know are two-host,
+ // else "eth%d" when there's reasonable doubt. userspace
+ // can rename the link if it knows better.
+ if ((dev->driver_info->flags & FLAG_ETHER) != 0 &&
+ ((dev->driver_info->flags & FLAG_POINTTOPOINT) == 0 ||
+ (net->dev_addr [0] & 0x02) == 0))
+ strcpy (net->name, "eth%d");
+ /* WLAN devices should always be named "wlan%d" */
+ if ((dev->driver_info->flags & FLAG_WLAN) != 0)
+ strcpy(net->name, "wlan%d");
+ /* WWAN devices should always be named "wwan%d" */
+ if ((dev->driver_info->flags & FLAG_WWAN) != 0)
+ strcpy(net->name, "wwan%d");
+
+ /* maybe the remote can't receive an Ethernet MTU */
+ if (net->mtu > (dev->hard_mtu - net->hard_header_len))
+ net->mtu = dev->hard_mtu - net->hard_header_len;
+ } else if (!info->in || !info->out)
+ status = usbnet_get_endpoints (dev, udev);
+ else {
+ dev->in = usb_rcvbulkpipe (xdev, info->in);
+ dev->out = usb_sndbulkpipe (xdev, info->out);
+ if (!(info->flags & FLAG_NO_SETINT))
+ status = usb_set_interface (xdev,
+ interface->desc.bInterfaceNumber,
+ interface->desc.bAlternateSetting);
+ else
+ status = 0;
+
+ }
+ if (status >= 0 && dev->status)
+ status = init_status (dev, udev);
+ if (status < 0)
+ goto out3;
+
+ if (!dev->rx_urb_size)
+ dev->rx_urb_size = dev->hard_mtu;
+ dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1);
+
+ if ((dev->driver_info->flags & FLAG_WLAN) != 0)
+ SET_NETDEV_DEVTYPE(net, &wlan_type);
+ if ((dev->driver_info->flags & FLAG_WWAN) != 0)
+ SET_NETDEV_DEVTYPE(net, &wwan_type);
+
+ status = register_netdev (net);
+ if (status)
+ goto out3;
+ netif_info(dev, probe, dev->net,
+ "register '%s' at usb-%s-%s, %s, %pM\n",
+ udev->dev.driver->name,
+ xdev->bus->bus_name, xdev->devpath,
+ dev->driver_info->description,
+ net->dev_addr);
+
+ // ok, it's ready to go.
+ usb_set_intfdata (udev, dev);
+
+ netif_device_attach (net);
+
+ if (dev->driver_info->flags & FLAG_LINK_INTR)
+ netif_carrier_off(net);
+
+ return 0;
+
+out3:
+ if (info->unbind)
+ info->unbind (dev, udev);
+out1:
+ free_netdev(net);
+out:
+ usb_put_dev(xdev);
+ return status;
+}
+EXPORT_SYMBOL_GPL(usbnet_probe);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * suspend the whole driver as soon as the first interface is suspended
+ * resume only when the last interface is resumed
+ */
+
+int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
+{
+ struct usbnet *dev = usb_get_intfdata(intf);
+
+ if (!dev->suspend_count++) {
+ spin_lock_irq(&dev->txq.lock);
+ /* don't autosuspend while transmitting */
+ if (dev->txq.qlen && PMSG_IS_AUTO(message)) {
+ spin_unlock_irq(&dev->txq.lock);
+ return -EBUSY;
+ } else {
+ set_bit(EVENT_DEV_ASLEEP, &dev->flags);
+ spin_unlock_irq(&dev->txq.lock);
+ }
+ /*
+ * accelerate emptying of the rx and queues, to avoid
+ * having everything error out.
+ */
+ netif_device_detach (dev->net);
+ usbnet_terminate_urbs(dev);
+ usb_kill_urb(dev->interrupt);
+
+ /*
+ * reattach so runtime management can use and
+ * wake the device
+ */
+ netif_device_attach (dev->net);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usbnet_suspend);
+
+int usbnet_resume (struct usb_interface *intf)
+{
+ struct usbnet *dev = usb_get_intfdata(intf);
+ struct sk_buff *skb;
+ struct urb *res;
+ int retval;
+
+ if (!--dev->suspend_count) {
+ /* resume interrupt URBs */
+ if (dev->interrupt && test_bit(EVENT_DEV_OPEN, &dev->flags))
+ usb_submit_urb(dev->interrupt, GFP_NOIO);
+
+ spin_lock_irq(&dev->txq.lock);
+ while ((res = usb_get_from_anchor(&dev->deferred))) {
+
+ skb = (struct sk_buff *)res->context;
+ retval = usb_submit_urb(res, GFP_ATOMIC);
+ if (retval < 0) {
+ dev_kfree_skb_any(skb);
+ usb_free_urb(res);
+ usb_autopm_put_interface_async(dev->intf);
+ } else {
+ dev->net->trans_start = jiffies;
+ __skb_queue_tail(&dev->txq, skb);
+ }
+ }
+
+ smp_mb();
+ clear_bit(EVENT_DEV_ASLEEP, &dev->flags);
+ spin_unlock_irq(&dev->txq.lock);
+
+ if (test_bit(EVENT_DEV_OPEN, &dev->flags)) {
+ if (!(dev->txq.qlen >= TX_QLEN(dev)))
+ netif_start_queue(dev->net);
+ tasklet_schedule (&dev->bh);
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usbnet_resume);
+
+
+/*-------------------------------------------------------------------------*/
+
+static int __init usbnet_init(void)
+{
+ /* Compiler should optimize this out. */
+ BUILD_BUG_ON(
+ FIELD_SIZEOF(struct sk_buff, cb) < sizeof(struct skb_data));
+
+ random_ether_addr(node_id);
+ return 0;
+}
+module_init(usbnet_init);
+
+static void __exit usbnet_exit(void)
+{
+}
+module_exit(usbnet_exit);
+
+MODULE_AUTHOR("David Brownell");
+MODULE_DESCRIPTION("USB network driver framework");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
new file mode 100644
index 0000000..f6b5a48
--- /dev/null
+++ b/drivers/net/wireless/Makefile
@@ -0,0 +1,48 @@
+#
+# Makefile for the Linux Wireless network device drivers.
+#
+
+obj-$(CONFIG_IPW2100) += ipw2x00/
+obj-$(CONFIG_IPW2200) += ipw2x00/
+
+obj-$(CONFIG_HERMES) += orinoco/
+
+obj-$(CONFIG_AT76C50X_USB) += at76c50x-usb.o
+
+obj-$(CONFIG_B43) += b43/
+obj-$(CONFIG_B43LEGACY) += b43legacy/
+obj-$(CONFIG_COMPAT_ZD1211RW) += zd1211rw/
+obj-$(CONFIG_RTL8180) += rtl818x/
+obj-$(CONFIG_RTL8187) += rtl818x/
+obj-$(CONFIG_RTLWIFI) += rtlwifi/
+
+obj-$(CONFIG_USB_NET_COMPAT_RNDIS_WLAN) += rndis_wlan.o
+
+obj-$(CONFIG_LIBERTAS) += libertas/
+
+obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/
+
+obj-$(CONFIG_ADM8211) += adm8211.o
+
+obj-$(CONFIG_MWL8K) += mwl8k.o
+
+obj-$(CONFIG_IWLWIFI) += iwlwifi/
+obj-$(CONFIG_IWLWIFI_LEGACY) += iwlegacy/
+obj-$(CONFIG_RT2X00) += rt2x00/
+
+obj-$(CONFIG_P54_COMMON) += p54/
+
+obj-$(CONFIG_ATH_COMMON) += ath/
+
+obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
+
+obj-$(CONFIG_WL1251) += wl1251/
+obj-$(CONFIG_WL12XX) += wl12xx/
+obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx/
+
+obj-$(CONFIG_IWM) += iwmc3200wifi/
+
+obj-$(CONFIG_MWIFIEX) += mwifiex/
+
+obj-$(CONFIG_BRCMFMAC) += brcm80211/
+obj-$(CONFIG_BRCMSMAC) += brcm80211/
diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c
new file mode 100644
index 0000000..fd7fc31
--- /dev/null
+++ b/drivers/net/wireless/adm8211.c
@@ -0,0 +1,2027 @@
+
+/*
+ * Linux device driver for ADMtek ADM8211 (IEEE 802.11b MAC/BBP)
+ *
+ * Copyright (c) 2003, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2007, Michael Wu <flamingice@sourmilk.net>
+ * Some parts copyright (c) 2003 by David Young <dyoung@pobox.com>
+ * and used with permission.
+ *
+ * Much thanks to Infineon-ADMtek for their support of this driver.
+ *
+ * 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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/if.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/etherdevice.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/crc32.h>
+#include <linux/eeprom_93cx6.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+
+#include "adm8211.h"
+
+MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
+MODULE_AUTHOR("Jouni Malinen <j@w1.fi>");
+MODULE_DESCRIPTION("Driver for IEEE 802.11b wireless cards based on ADMtek ADM8211");
+MODULE_SUPPORTED_DEVICE("ADM8211");
+MODULE_LICENSE("GPL");
+
+static unsigned int tx_ring_size __read_mostly = 16;
+static unsigned int rx_ring_size __read_mostly = 16;
+
+module_param(tx_ring_size, uint, 0);
+module_param(rx_ring_size, uint, 0);
+
+static DEFINE_PCI_DEVICE_TABLE(adm8211_pci_id_table) = {
+ /* ADMtek ADM8211 */
+ { PCI_DEVICE(0x10B7, 0x6000) }, /* 3Com 3CRSHPW796 */
+ { PCI_DEVICE(0x1200, 0x8201) }, /* ? */
+ { PCI_DEVICE(0x1317, 0x8201) }, /* ADM8211A */
+ { PCI_DEVICE(0x1317, 0x8211) }, /* ADM8211B/C */
+ { 0 }
+};
+
+static struct ieee80211_rate adm8211_rates[] = {
+ { .bitrate = 10, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 220, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, /* XX ?? */
+};
+
+static const struct ieee80211_channel adm8211_channels[] = {
+ { .center_freq = 2412},
+ { .center_freq = 2417},
+ { .center_freq = 2422},
+ { .center_freq = 2427},
+ { .center_freq = 2432},
+ { .center_freq = 2437},
+ { .center_freq = 2442},
+ { .center_freq = 2447},
+ { .center_freq = 2452},
+ { .center_freq = 2457},
+ { .center_freq = 2462},
+ { .center_freq = 2467},
+ { .center_freq = 2472},
+ { .center_freq = 2484},
+};
+
+
+static void adm8211_eeprom_register_read(struct eeprom_93cx6 *eeprom)
+{
+ struct adm8211_priv *priv = eeprom->data;
+ u32 reg = ADM8211_CSR_READ(SPR);
+
+ eeprom->reg_data_in = reg & ADM8211_SPR_SDI;
+ eeprom->reg_data_out = reg & ADM8211_SPR_SDO;
+ eeprom->reg_data_clock = reg & ADM8211_SPR_SCLK;
+ eeprom->reg_chip_select = reg & ADM8211_SPR_SCS;
+}
+
+static void adm8211_eeprom_register_write(struct eeprom_93cx6 *eeprom)
+{
+ struct adm8211_priv *priv = eeprom->data;
+ u32 reg = 0x4000 | ADM8211_SPR_SRS;
+
+ if (eeprom->reg_data_in)
+ reg |= ADM8211_SPR_SDI;
+ if (eeprom->reg_data_out)
+ reg |= ADM8211_SPR_SDO;
+ if (eeprom->reg_data_clock)
+ reg |= ADM8211_SPR_SCLK;
+ if (eeprom->reg_chip_select)
+ reg |= ADM8211_SPR_SCS;
+
+ ADM8211_CSR_WRITE(SPR, reg);
+ ADM8211_CSR_READ(SPR); /* eeprom_delay */
+}
+
+static int adm8211_read_eeprom(struct ieee80211_hw *dev)
+{
+ struct adm8211_priv *priv = dev->priv;
+ unsigned int words, i;
+ struct ieee80211_chan_range chan_range;
+ u16 cr49;
+ struct eeprom_93cx6 eeprom = {
+ .data = priv,
+ .register_read = adm8211_eeprom_register_read,
+ .register_write = adm8211_eeprom_register_write
+ };
+
+ if (ADM8211_CSR_READ(CSR_TEST0) & ADM8211_CSR_TEST0_EPTYP) {
+ /* 256 * 16-bit = 512 bytes */
+ eeprom.width = PCI_EEPROM_WIDTH_93C66;
+ words = 256;
+ } else {
+ /* 64 * 16-bit = 128 bytes */
+ eeprom.width = PCI_EEPROM_WIDTH_93C46;
+ words = 64;
+ }
+
+ priv->eeprom_len = words * 2;
+ priv->eeprom = kmalloc(priv->eeprom_len, GFP_KERNEL);
+ if (!priv->eeprom)
+ return -ENOMEM;
+
+ eeprom_93cx6_multiread(&eeprom, 0, (__le16 *)priv->eeprom, words);
+
+ cr49 = le16_to_cpu(priv->eeprom->cr49);
+ priv->rf_type = (cr49 >> 3) & 0x7;
+ switch (priv->rf_type) {
+ case ADM8211_TYPE_INTERSIL:
+ case ADM8211_TYPE_RFMD:
+ case ADM8211_TYPE_MARVEL:
+ case ADM8211_TYPE_AIROHA:
+ case ADM8211_TYPE_ADMTEK:
+ break;
+
+ default:
+ if (priv->pdev->revision < ADM8211_REV_CA)
+ priv->rf_type = ADM8211_TYPE_RFMD;
+ else
+ priv->rf_type = ADM8211_TYPE_AIROHA;
+
+ printk(KERN_WARNING "%s (adm8211): Unknown RFtype %d\n",
+ pci_name(priv->pdev), (cr49 >> 3) & 0x7);
+ }
+
+ priv->bbp_type = cr49 & 0x7;
+ switch (priv->bbp_type) {
+ case ADM8211_TYPE_INTERSIL:
+ case ADM8211_TYPE_RFMD:
+ case ADM8211_TYPE_MARVEL:
+ case ADM8211_TYPE_AIROHA:
+ case ADM8211_TYPE_ADMTEK:
+ break;
+ default:
+ if (priv->pdev->revision < ADM8211_REV_CA)
+ priv->bbp_type = ADM8211_TYPE_RFMD;
+ else
+ priv->bbp_type = ADM8211_TYPE_ADMTEK;
+
+ printk(KERN_WARNING "%s (adm8211): Unknown BBPtype: %d\n",
+ pci_name(priv->pdev), cr49 >> 3);
+ }
+
+ if (priv->eeprom->country_code >= ARRAY_SIZE(cranges)) {
+ printk(KERN_WARNING "%s (adm8211): Invalid country code (%d)\n",
+ pci_name(priv->pdev), priv->eeprom->country_code);
+
+ chan_range = cranges[2];
+ } else
+ chan_range = cranges[priv->eeprom->country_code];
+
+ printk(KERN_DEBUG "%s (adm8211): Channel range: %d - %d\n",
+ pci_name(priv->pdev), (int)chan_range.min, (int)chan_range.max);
+
+ BUILD_BUG_ON(sizeof(priv->channels) != sizeof(adm8211_channels));
+
+ memcpy(priv->channels, adm8211_channels, sizeof(priv->channels));
+ priv->band.channels = priv->channels;
+ priv->band.n_channels = ARRAY_SIZE(adm8211_channels);
+ priv->band.bitrates = adm8211_rates;
+ priv->band.n_bitrates = ARRAY_SIZE(adm8211_rates);
+
+ for (i = 1; i <= ARRAY_SIZE(adm8211_channels); i++)
+ if (i < chan_range.min || i > chan_range.max)
+ priv->channels[i - 1].flags |= IEEE80211_CHAN_DISABLED;
+
+ switch (priv->eeprom->specific_bbptype) {
+ case ADM8211_BBP_RFMD3000:
+ case ADM8211_BBP_RFMD3002:
+ case ADM8211_BBP_ADM8011:
+ priv->specific_bbptype = priv->eeprom->specific_bbptype;
+ break;
+
+ default:
+ if (priv->pdev->revision < ADM8211_REV_CA)
+ priv->specific_bbptype = ADM8211_BBP_RFMD3000;
+ else
+ priv->specific_bbptype = ADM8211_BBP_ADM8011;
+
+ printk(KERN_WARNING "%s (adm8211): Unknown specific BBP: %d\n",
+ pci_name(priv->pdev), priv->eeprom->specific_bbptype);
+ }
+
+ switch (priv->eeprom->specific_rftype) {
+ case ADM8211_RFMD2948:
+ case ADM8211_RFMD2958:
+ case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
+ case ADM8211_MAX2820:
+ case ADM8211_AL2210L:
+ priv->transceiver_type = priv->eeprom->specific_rftype;
+ break;
+
+ default:
+ if (priv->pdev->revision == ADM8211_REV_BA)
+ priv->transceiver_type = ADM8211_RFMD2958_RF3000_CONTROL_POWER;
+ else if (priv->pdev->revision == ADM8211_REV_CA)
+ priv->transceiver_type = ADM8211_AL2210L;
+ else if (priv->pdev->revision == ADM8211_REV_AB)
+ priv->transceiver_type = ADM8211_RFMD2948;
+
+ printk(KERN_WARNING "%s (adm8211): Unknown transceiver: %d\n",
+ pci_name(priv->pdev), priv->eeprom->specific_rftype);
+
+ break;
+ }
+
+ printk(KERN_DEBUG "%s (adm8211): RFtype=%d BBPtype=%d Specific BBP=%d "
+ "Transceiver=%d\n", pci_name(priv->pdev), priv->rf_type,
+ priv->bbp_type, priv->specific_bbptype, priv->transceiver_type);
+
+ return 0;
+}
+
+static inline void adm8211_write_sram(struct ieee80211_hw *dev,
+ u32 addr, u32 data)
+{
+ struct adm8211_priv *priv = dev->priv;
+
+ ADM8211_CSR_WRITE(WEPCTL, addr | ADM8211_WEPCTL_TABLE_WR |
+ (priv->pdev->revision < ADM8211_REV_BA ?
+ 0 : ADM8211_WEPCTL_SEL_WEPTABLE ));
+ ADM8211_CSR_READ(WEPCTL);
+ msleep(1);
+
+ ADM8211_CSR_WRITE(WESK, data);
+ ADM8211_CSR_READ(WESK);
+ msleep(1);
+}
+
+static void adm8211_write_sram_bytes(struct ieee80211_hw *dev,
+ unsigned int addr, u8 *buf,
+ unsigned int len)
+{
+ struct adm8211_priv *priv = dev->priv;
+ u32 reg = ADM8211_CSR_READ(WEPCTL);
+ unsigned int i;
+
+ if (priv->pdev->revision < ADM8211_REV_BA) {
+ for (i = 0; i < len; i += 2) {
+ u16 val = buf[i] | (buf[i + 1] << 8);
+ adm8211_write_sram(dev, addr + i / 2, val);
+ }
+ } else {
+ for (i = 0; i < len; i += 4) {
+ u32 val = (buf[i + 0] << 0 ) | (buf[i + 1] << 8 ) |
+ (buf[i + 2] << 16) | (buf[i + 3] << 24);
+ adm8211_write_sram(dev, addr + i / 4, val);
+ }
+ }
+
+ ADM8211_CSR_WRITE(WEPCTL, reg);
+}
+
+static void adm8211_clear_sram(struct ieee80211_hw *dev)
+{
+ struct adm8211_priv *priv = dev->priv;
+ u32 reg = ADM8211_CSR_READ(WEPCTL);
+ unsigned int addr;
+
+ for (addr = 0; addr < ADM8211_SRAM_SIZE; addr++)
+ adm8211_write_sram(dev, addr, 0);
+
+ ADM8211_CSR_WRITE(WEPCTL, reg);
+}
+
+static int adm8211_get_stats(struct ieee80211_hw *dev,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct adm8211_priv *priv = dev->priv;
+
+ memcpy(stats, &priv->stats, sizeof(*stats));
+
+ return 0;
+}
+
+static void adm8211_interrupt_tci(struct ieee80211_hw *dev)
+{
+ struct adm8211_priv *priv = dev->priv;
+ unsigned int dirty_tx;
+
+ spin_lock(&priv->lock);
+
+ for (dirty_tx = priv->dirty_tx; priv->cur_tx - dirty_tx; dirty_tx++) {
+ unsigned int entry = dirty_tx % priv->tx_ring_size;
+ u32 status = le32_to_cpu(priv->tx_ring[entry].status);
+ struct ieee80211_tx_info *txi;
+ struct adm8211_tx_ring_info *info;
+ struct sk_buff *skb;
+
+ if (status & TDES0_CONTROL_OWN ||
+ !(status & TDES0_CONTROL_DONE))
+ break;
+
+ info = &priv->tx_buffers[entry];
+ skb = info->skb;
+ txi = IEEE80211_SKB_CB(skb);
+
+ /* TODO: check TDES0_STATUS_TUF and TDES0_STATUS_TRO */
+
+ pci_unmap_single(priv->pdev, info->mapping,
+ info->skb->len, PCI_DMA_TODEVICE);
+
+ ieee80211_tx_info_clear_status(txi);
+
+ skb_pull(skb, sizeof(struct adm8211_tx_hdr));
+ memcpy(skb_push(skb, info->hdrlen), skb->cb, info->hdrlen);
+ if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) &&
+ !(status & TDES0_STATUS_ES))
+ txi->flags |= IEEE80211_TX_STAT_ACK;
+
+ ieee80211_tx_status_irqsafe(dev, skb);
+
+ info->skb = NULL;
+ }
+
+ if (priv->cur_tx - dirty_tx < priv->tx_ring_size - 2)
+ ieee80211_wake_queue(dev, 0);
+
+ priv->dirty_tx = dirty_tx;
+ spin_unlock(&priv->lock);
+}
+
+
+static void adm8211_interrupt_rci(struct ieee80211_hw *dev)
+{
+ struct adm8211_priv *priv = dev->priv;
+ unsigned int entry = priv->cur_rx % priv->rx_ring_size;
+ u32 status;
+ unsigned int pktlen;
+ struct sk_buff *skb, *newskb;
+ unsigned int limit = priv->rx_ring_size;
+ u8 rssi, rate;
+
+ while (!(priv->rx_ring[entry].status & cpu_to_le32(RDES0_STATUS_OWN))) {
+ if (!limit--)
+ break;
+
+ status = le32_to_cpu(priv->rx_ring[entry].status);
+ rate = (status & RDES0_STATUS_RXDR) >> 12;
+ rssi = le32_to_cpu(priv->rx_ring[entry].length) &
+ RDES1_STATUS_RSSI;
+
+ pktlen = status & RDES0_STATUS_FL;
+ if (pktlen > RX_PKT_SIZE) {
+ if (net_ratelimit())
+ wiphy_debug(dev->wiphy, "frame too long (%d)\n",
+ pktlen);
+ pktlen = RX_PKT_SIZE;
+ }
+
+ if (!priv->soft_rx_crc && status & RDES0_STATUS_ES) {
+ skb = NULL; /* old buffer will be reused */
+ /* TODO: update RX error stats */
+ /* TODO: check RDES0_STATUS_CRC*E */
+ } else if (pktlen < RX_COPY_BREAK) {
+ skb = dev_alloc_skb(pktlen);
+ if (skb) {
+ pci_dma_sync_single_for_cpu(
+ priv->pdev,
+ priv->rx_buffers[entry].mapping,
+ pktlen, PCI_DMA_FROMDEVICE);
+ memcpy(skb_put(skb, pktlen),
+ skb_tail_pointer(priv->rx_buffers[entry].skb),
+ pktlen);
+ pci_dma_sync_single_for_device(
+ priv->pdev,
+ priv->rx_buffers[entry].mapping,
+ RX_PKT_SIZE, PCI_DMA_FROMDEVICE);
+ }
+ } else {
+ newskb = dev_alloc_skb(RX_PKT_SIZE);
+ if (newskb) {
+ skb = priv->rx_buffers[entry].skb;
+ skb_put(skb, pktlen);
+ pci_unmap_single(
+ priv->pdev,
+ priv->rx_buffers[entry].mapping,
+ RX_PKT_SIZE, PCI_DMA_FROMDEVICE);
+ priv->rx_buffers[entry].skb = newskb;
+ priv->rx_buffers[entry].mapping =
+ pci_map_single(priv->pdev,
+ skb_tail_pointer(newskb),
+ RX_PKT_SIZE,
+ PCI_DMA_FROMDEVICE);
+ } else {
+ skb = NULL;
+ /* TODO: update rx dropped stats */
+ }
+
+ priv->rx_ring[entry].buffer1 =
+ cpu_to_le32(priv->rx_buffers[entry].mapping);
+ }
+
+ priv->rx_ring[entry].status = cpu_to_le32(RDES0_STATUS_OWN |
+ RDES0_STATUS_SQL);
+ priv->rx_ring[entry].length =
+ cpu_to_le32(RX_PKT_SIZE |
+ (entry == priv->rx_ring_size - 1 ?
+ RDES1_CONTROL_RER : 0));
+
+ if (skb) {
+ struct ieee80211_rx_status rx_status = {0};
+
+ if (priv->pdev->revision < ADM8211_REV_CA)
+ rx_status.signal = rssi;
+ else
+ rx_status.signal = 100 - rssi;
+
+ rx_status.rate_idx = rate;
+
+ rx_status.freq = adm8211_channels[priv->channel - 1].center_freq;
+ rx_status.band = IEEE80211_BAND_2GHZ;
+
+ memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
+ ieee80211_rx_irqsafe(dev, skb);
+ }
+
+ entry = (++priv->cur_rx) % priv->rx_ring_size;
+ }
+
+ /* TODO: check LPC and update stats? */
+}
+
+
+static irqreturn_t adm8211_interrupt(int irq, void *dev_id)
+{
+#define ADM8211_INT(x) \
+do { \
+ if (unlikely(stsr & ADM8211_STSR_ ## x)) \
+ wiphy_debug(dev->wiphy, "%s\n", #x); \
+} while (0)
+
+ struct ieee80211_hw *dev = dev_id;
+ struct adm8211_priv *priv = dev->priv;
+ u32 stsr = ADM8211_CSR_READ(STSR);
+ ADM8211_CSR_WRITE(STSR, stsr);
+ if (stsr == 0xffffffff)
+ return IRQ_HANDLED;
+
+ if (!(stsr & (ADM8211_STSR_NISS | ADM8211_STSR_AISS)))
+ return IRQ_HANDLED;
+
+ if (stsr & ADM8211_STSR_RCI)
+ adm8211_interrupt_rci(dev);
+ if (stsr & ADM8211_STSR_TCI)
+ adm8211_interrupt_tci(dev);
+
+ ADM8211_INT(PCF);
+ ADM8211_INT(BCNTC);
+ ADM8211_INT(GPINT);
+ ADM8211_INT(ATIMTC);
+ ADM8211_INT(TSFTF);
+ ADM8211_INT(TSCZ);
+ ADM8211_INT(SQL);
+ ADM8211_INT(WEPTD);
+ ADM8211_INT(ATIME);
+ ADM8211_INT(TEIS);
+ ADM8211_INT(FBE);
+ ADM8211_INT(REIS);
+ ADM8211_INT(GPTT);
+ ADM8211_INT(RPS);
+ ADM8211_INT(RDU);
+ ADM8211_INT(TUF);
+ ADM8211_INT(TPS);
+
+ return IRQ_HANDLED;
+
+#undef ADM8211_INT
+}
+
+#define WRITE_SYN(name,v_mask,v_shift,a_mask,a_shift,bits,prewrite,postwrite)\
+static void adm8211_rf_write_syn_ ## name (struct ieee80211_hw *dev, \
+ u16 addr, u32 value) { \
+ struct adm8211_priv *priv = dev->priv; \
+ unsigned int i; \
+ u32 reg, bitbuf; \
+ \
+ value &= v_mask; \
+ addr &= a_mask; \
+ bitbuf = (value << v_shift) | (addr << a_shift); \
+ \
+ ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_1); \
+ ADM8211_CSR_READ(SYNRF); \
+ ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_0); \
+ ADM8211_CSR_READ(SYNRF); \
+ \
+ if (prewrite) { \
+ ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_WRITE_SYNDATA_0); \
+ ADM8211_CSR_READ(SYNRF); \
+ } \
+ \
+ for (i = 0; i <= bits; i++) { \
+ if (bitbuf & (1 << (bits - i))) \
+ reg = ADM8211_SYNRF_WRITE_SYNDATA_1; \
+ else \
+ reg = ADM8211_SYNRF_WRITE_SYNDATA_0; \
+ \
+ ADM8211_CSR_WRITE(SYNRF, reg); \
+ ADM8211_CSR_READ(SYNRF); \
+ \
+ ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_1); \
+ ADM8211_CSR_READ(SYNRF); \
+ ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_0); \
+ ADM8211_CSR_READ(SYNRF); \
+ } \
+ \
+ if (postwrite == 1) { \
+ ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_0); \
+ ADM8211_CSR_READ(SYNRF); \
+ } \
+ if (postwrite == 2) { \
+ ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_1); \
+ ADM8211_CSR_READ(SYNRF); \
+ } \
+ \
+ ADM8211_CSR_WRITE(SYNRF, 0); \
+ ADM8211_CSR_READ(SYNRF); \
+}
+
+WRITE_SYN(max2820, 0x00FFF, 0, 0x0F, 12, 15, 1, 1)
+WRITE_SYN(al2210l, 0xFFFFF, 4, 0x0F, 0, 23, 1, 1)
+WRITE_SYN(rfmd2958, 0x3FFFF, 0, 0x1F, 18, 23, 0, 1)
+WRITE_SYN(rfmd2948, 0x0FFFF, 4, 0x0F, 0, 21, 0, 2)
+
+#undef WRITE_SYN
+
+static int adm8211_write_bbp(struct ieee80211_hw *dev, u8 addr, u8 data)
+{
+ struct adm8211_priv *priv = dev->priv;
+ unsigned int timeout;
+ u32 reg;
+
+ timeout = 10;
+ while (timeout > 0) {
+ reg = ADM8211_CSR_READ(BBPCTL);
+ if (!(reg & (ADM8211_BBPCTL_WR | ADM8211_BBPCTL_RD)))
+ break;
+ timeout--;
+ msleep(2);
+ }
+
+ if (timeout == 0) {
+ wiphy_debug(dev->wiphy,
+ "adm8211_write_bbp(%d,%d) failed prewrite (reg=0x%08x)\n",
+ addr, data, reg);
+ return -ETIMEDOUT;
+ }
+
+ switch (priv->bbp_type) {
+ case ADM8211_TYPE_INTERSIL:
+ reg = ADM8211_BBPCTL_MMISEL; /* three wire interface */
+ break;
+ case ADM8211_TYPE_RFMD:
+ reg = (0x20 << 24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP |
+ (0x01 << 18);
+ break;
+ case ADM8211_TYPE_ADMTEK:
+ reg = (0x20 << 24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP |
+ (0x05 << 18);
+ break;
+ }
+ reg |= ADM8211_BBPCTL_WR | (addr << 8) | data;
+
+ ADM8211_CSR_WRITE(BBPCTL, reg);
+
+ timeout = 10;
+ while (timeout > 0) {
+ reg = ADM8211_CSR_READ(BBPCTL);
+ if (!(reg & ADM8211_BBPCTL_WR))
+ break;
+ timeout--;
+ msleep(2);
+ }
+
+ if (timeout == 0) {
+ ADM8211_CSR_WRITE(BBPCTL, ADM8211_CSR_READ(BBPCTL) &
+ ~ADM8211_BBPCTL_WR);
+ wiphy_debug(dev->wiphy,
+ "adm8211_write_bbp(%d,%d) failed postwrite (reg=0x%08x)\n",
+ addr, data, reg);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int adm8211_rf_set_channel(struct ieee80211_hw *dev, unsigned int chan)
+{
+ static const u32 adm8211_rfmd2958_reg5[] =
+ {0x22BD, 0x22D2, 0x22E8, 0x22FE, 0x2314, 0x232A, 0x2340,
+ 0x2355, 0x236B, 0x2381, 0x2397, 0x23AD, 0x23C2, 0x23F7};
+ static const u32 adm8211_rfmd2958_reg6[] =
+ {0x05D17, 0x3A2E8, 0x2E8BA, 0x22E8B, 0x1745D, 0x0BA2E, 0x00000,
+ 0x345D1, 0x28BA2, 0x1D174, 0x11745, 0x05D17, 0x3A2E8, 0x11745};
+
+ struct adm8211_priv *priv = dev->priv;
+ u8 ant_power = priv->ant_power > 0x3F ?
+ priv->eeprom->antenna_power[chan - 1] : priv->ant_power;
+ u8 tx_power = priv->tx_power > 0x3F ?
+ priv->eeprom->tx_power[chan - 1] : priv->tx_power;
+ u8 lpf_cutoff = priv->lpf_cutoff == 0xFF ?
+ priv->eeprom->lpf_cutoff[chan - 1] : priv->lpf_cutoff;
+ u8 lnags_thresh = priv->lnags_threshold == 0xFF ?
+ priv->eeprom->lnags_threshold[chan - 1] : priv->lnags_threshold;
+ u32 reg;
+
+ ADM8211_IDLE();
+
+ /* Program synthesizer to new channel */
+ switch (priv->transceiver_type) {
+ case ADM8211_RFMD2958:
+ case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
+ adm8211_rf_write_syn_rfmd2958(dev, 0x00, 0x04007);
+ adm8211_rf_write_syn_rfmd2958(dev, 0x02, 0x00033);
+
+ adm8211_rf_write_syn_rfmd2958(dev, 0x05,
+ adm8211_rfmd2958_reg5[chan - 1]);
+ adm8211_rf_write_syn_rfmd2958(dev, 0x06,
+ adm8211_rfmd2958_reg6[chan - 1]);
+ break;
+
+ case ADM8211_RFMD2948:
+ adm8211_rf_write_syn_rfmd2948(dev, SI4126_MAIN_CONF,
+ SI4126_MAIN_XINDIV2);
+ adm8211_rf_write_syn_rfmd2948(dev, SI4126_POWERDOWN,
+ SI4126_POWERDOWN_PDIB |
+ SI4126_POWERDOWN_PDRB);
+ adm8211_rf_write_syn_rfmd2948(dev, SI4126_PHASE_DET_GAIN, 0);
+ adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_N_DIV,
+ (chan == 14 ?
+ 2110 : (2033 + (chan * 5))));
+ adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_N_DIV, 1496);
+ adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_R_DIV, 44);
+ adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_R_DIV, 44);
+ break;
+
+ case ADM8211_MAX2820:
+ adm8211_rf_write_syn_max2820(dev, 0x3,
+ (chan == 14 ? 0x054 : (0x7 + (chan * 5))));
+ break;
+
+ case ADM8211_AL2210L:
+ adm8211_rf_write_syn_al2210l(dev, 0x0,
+ (chan == 14 ? 0x229B4 : (0x22967 + (chan * 5))));
+ break;
+
+ default:
+ wiphy_debug(dev->wiphy, "unsupported transceiver type %d\n",
+ priv->transceiver_type);
+ break;
+ }
+
+ /* write BBP regs */
+ if (priv->bbp_type == ADM8211_TYPE_RFMD) {
+
+ /* SMC 2635W specific? adm8211b doesn't use the 2948 though.. */
+ /* TODO: remove if SMC 2635W doesn't need this */
+ if (priv->transceiver_type == ADM8211_RFMD2948) {
+ reg = ADM8211_CSR_READ(GPIO);
+ reg &= 0xfffc0000;
+ reg |= ADM8211_CSR_GPIO_EN0;
+ if (chan != 14)
+ reg |= ADM8211_CSR_GPIO_O0;
+ ADM8211_CSR_WRITE(GPIO, reg);
+ }
+
+ if (priv->transceiver_type == ADM8211_RFMD2958) {
+ /* set PCNT2 */
+ adm8211_rf_write_syn_rfmd2958(dev, 0x0B, 0x07100);
+ /* set PCNT1 P_DESIRED/MID_BIAS */
+ reg = le16_to_cpu(priv->eeprom->cr49);
+ reg >>= 13;
+ reg <<= 15;
+ reg |= ant_power << 9;
+ adm8211_rf_write_syn_rfmd2958(dev, 0x0A, reg);
+ /* set TXRX TX_GAIN */
+ adm8211_rf_write_syn_rfmd2958(dev, 0x09, 0x00050 |
+ (priv->pdev->revision < ADM8211_REV_CA ? tx_power : 0));
+ } else {
+ reg = ADM8211_CSR_READ(PLCPHD);
+ reg &= 0xff00ffff;
+ reg |= tx_power << 18;
+ ADM8211_CSR_WRITE(PLCPHD, reg);
+ }
+
+ ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_SELRF |
+ ADM8211_SYNRF_PE1 | ADM8211_SYNRF_PHYRST);
+ ADM8211_CSR_READ(SYNRF);
+ msleep(30);
+
+ /* RF3000 BBP */
+ if (priv->transceiver_type != ADM8211_RFMD2958)
+ adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT,
+ tx_power<<2);
+ adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, lpf_cutoff);
+ adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, lnags_thresh);
+ adm8211_write_bbp(dev, 0x1c, priv->pdev->revision == ADM8211_REV_BA ?
+ priv->eeprom->cr28 : 0);
+ adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29);
+
+ ADM8211_CSR_WRITE(SYNRF, 0);
+
+ /* Nothing to do for ADMtek BBP */
+ } else if (priv->bbp_type != ADM8211_TYPE_ADMTEK)
+ wiphy_debug(dev->wiphy, "unsupported BBP type %d\n",
+ priv->bbp_type);
+
+ ADM8211_RESTORE();
+
+ /* update current channel for adhoc (and maybe AP mode) */
+ reg = ADM8211_CSR_READ(CAP0);
+ reg &= ~0xF;
+ reg |= chan;
+ ADM8211_CSR_WRITE(CAP0, reg);
+
+ return 0;
+}
+
+static void adm8211_update_mode(struct ieee80211_hw *dev)
+{
+ struct adm8211_priv *priv = dev->priv;
+
+ ADM8211_IDLE();
+
+ priv->soft_rx_crc = 0;
+ switch (priv->mode) {
+ case NL80211_IFTYPE_STATION:
+ priv->nar &= ~(ADM8211_NAR_PR | ADM8211_NAR_EA);
+ priv->nar |= ADM8211_NAR_ST | ADM8211_NAR_SR;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ priv->nar &= ~ADM8211_NAR_PR;
+ priv->nar |= ADM8211_NAR_EA | ADM8211_NAR_ST | ADM8211_NAR_SR;
+
+ /* don't trust the error bits on rev 0x20 and up in adhoc */
+ if (priv->pdev->revision >= ADM8211_REV_BA)
+ priv->soft_rx_crc = 1;
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ priv->nar &= ~(ADM8211_NAR_EA | ADM8211_NAR_ST);
+ priv->nar |= ADM8211_NAR_PR | ADM8211_NAR_SR;
+ break;
+ }
+
+ ADM8211_RESTORE();
+}
+
+static void adm8211_hw_init_syn(struct ieee80211_hw *dev)
+{
+ struct adm8211_priv *priv = dev->priv;
+
+ switch (priv->transceiver_type) {
+ case ADM8211_RFMD2958:
+ case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
+ /* comments taken from ADMtek vendor driver */
+
+ /* Reset RF2958 after power on */
+ adm8211_rf_write_syn_rfmd2958(dev, 0x1F, 0x00000);
+ /* Initialize RF VCO Core Bias to maximum */
+ adm8211_rf_write_syn_rfmd2958(dev, 0x0C, 0x3001F);
+ /* Initialize IF PLL */
+ adm8211_rf_write_syn_rfmd2958(dev, 0x01, 0x29C03);
+ /* Initialize IF PLL Coarse Tuning */
+ adm8211_rf_write_syn_rfmd2958(dev, 0x03, 0x1FF6F);
+ /* Initialize RF PLL */
+ adm8211_rf_write_syn_rfmd2958(dev, 0x04, 0x29403);
+ /* Initialize RF PLL Coarse Tuning */
+ adm8211_rf_write_syn_rfmd2958(dev, 0x07, 0x1456F);
+ /* Initialize TX gain and filter BW (R9) */
+ adm8211_rf_write_syn_rfmd2958(dev, 0x09,
+ (priv->transceiver_type == ADM8211_RFMD2958 ?
+ 0x10050 : 0x00050));
+ /* Initialize CAL register */
+ adm8211_rf_write_syn_rfmd2958(dev, 0x08, 0x3FFF8);
+ break;
+
+ case ADM8211_MAX2820:
+ adm8211_rf_write_syn_max2820(dev, 0x1, 0x01E);
+ adm8211_rf_write_syn_max2820(dev, 0x2, 0x001);
+ adm8211_rf_write_syn_max2820(dev, 0x3, 0x054);
+ adm8211_rf_write_syn_max2820(dev, 0x4, 0x310);
+ adm8211_rf_write_syn_max2820(dev, 0x5, 0x000);
+ break;
+
+ case ADM8211_AL2210L:
+ adm8211_rf_write_syn_al2210l(dev, 0x0, 0x0196C);
+ adm8211_rf_write_syn_al2210l(dev, 0x1, 0x007CB);
+ adm8211_rf_write_syn_al2210l(dev, 0x2, 0x3582F);
+ adm8211_rf_write_syn_al2210l(dev, 0x3, 0x010A9);
+ adm8211_rf_write_syn_al2210l(dev, 0x4, 0x77280);
+ adm8211_rf_write_syn_al2210l(dev, 0x5, 0x45641);
+ adm8211_rf_write_syn_al2210l(dev, 0x6, 0xEA130);
+ adm8211_rf_write_syn_al2210l(dev, 0x7, 0x80000);
+ adm8211_rf_write_syn_al2210l(dev, 0x8, 0x7850F);
+ adm8211_rf_write_syn_al2210l(dev, 0x9, 0xF900C);
+ adm8211_rf_write_syn_al2210l(dev, 0xA, 0x00000);
+ adm8211_rf_write_syn_al2210l(dev, 0xB, 0x00000);
+ break;
+
+ case ADM8211_RFMD2948:
+ default:
+ break;
+ }
+}
+
+static int adm8211_hw_init_bbp(struct ieee80211_hw *dev)
+{
+ struct adm8211_priv *priv = dev->priv;
+ u32 reg;
+
+ /* write addresses */
+ if (priv->bbp_type == ADM8211_TYPE_INTERSIL) {
+ ADM8211_CSR_WRITE(MMIWA, 0x100E0C0A);
+ ADM8211_CSR_WRITE(MMIRD0, 0x00007C7E);
+ ADM8211_CSR_WRITE(MMIRD1, 0x00100000);
+ } else if (priv->bbp_type == ADM8211_TYPE_RFMD ||
+ priv->bbp_type == ADM8211_TYPE_ADMTEK) {
+ /* check specific BBP type */
+ switch (priv->specific_bbptype) {
+ case ADM8211_BBP_RFMD3000:
+ case ADM8211_BBP_RFMD3002:
+ ADM8211_CSR_WRITE(MMIWA, 0x00009101);
+ ADM8211_CSR_WRITE(MMIRD0, 0x00000301);
+ break;
+
+ case ADM8211_BBP_ADM8011:
+ ADM8211_CSR_WRITE(MMIWA, 0x00008903);
+ ADM8211_CSR_WRITE(MMIRD0, 0x00001716);
+
+ reg = ADM8211_CSR_READ(BBPCTL);
+ reg &= ~ADM8211_BBPCTL_TYPE;
+ reg |= 0x5 << 18;
+ ADM8211_CSR_WRITE(BBPCTL, reg);
+ break;
+ }
+
+ switch (priv->pdev->revision) {
+ case ADM8211_REV_CA:
+ if (priv->transceiver_type == ADM8211_RFMD2958 ||
+ priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER ||
+ priv->transceiver_type == ADM8211_RFMD2948)
+ ADM8211_CSR_WRITE(SYNCTL, 0x1 << 22);
+ else if (priv->transceiver_type == ADM8211_MAX2820 ||
+ priv->transceiver_type == ADM8211_AL2210L)
+ ADM8211_CSR_WRITE(SYNCTL, 0x3 << 22);
+ break;
+
+ case ADM8211_REV_BA:
+ reg = ADM8211_CSR_READ(MMIRD1);
+ reg &= 0x0000FFFF;
+ reg |= 0x7e100000;
+ ADM8211_CSR_WRITE(MMIRD1, reg);
+ break;
+
+ case ADM8211_REV_AB:
+ case ADM8211_REV_AF:
+ default:
+ ADM8211_CSR_WRITE(MMIRD1, 0x7e100000);
+ break;
+ }
+
+ /* For RFMD */
+ ADM8211_CSR_WRITE(MACTEST, 0x800);
+ }
+
+ adm8211_hw_init_syn(dev);
+
+ /* Set RF Power control IF pin to PE1+PHYRST# */
+ ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_SELRF |
+ ADM8211_SYNRF_PE1 | ADM8211_SYNRF_PHYRST);
+ ADM8211_CSR_READ(SYNRF);
+ msleep(20);
+
+ /* write BBP regs */
+ if (priv->bbp_type == ADM8211_TYPE_RFMD) {
+ /* RF3000 BBP */
+ /* another set:
+ * 11: c8
+ * 14: 14
+ * 15: 50 (chan 1..13; chan 14: d0)
+ * 1c: 00
+ * 1d: 84
+ */
+ adm8211_write_bbp(dev, RF3000_CCA_CTRL, 0x80);
+ /* antenna selection: diversity */
+ adm8211_write_bbp(dev, RF3000_DIVERSITY__RSSI, 0x80);
+ adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT, 0x74);
+ adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, 0x38);
+ adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, 0x40);
+
+ if (priv->eeprom->major_version < 2) {
+ adm8211_write_bbp(dev, 0x1c, 0x00);
+ adm8211_write_bbp(dev, 0x1d, 0x80);
+ } else {
+ if (priv->pdev->revision == ADM8211_REV_BA)
+ adm8211_write_bbp(dev, 0x1c, priv->eeprom->cr28);
+ else
+ adm8211_write_bbp(dev, 0x1c, 0x00);
+
+ adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29);
+ }
+ } else if (priv->bbp_type == ADM8211_TYPE_ADMTEK) {
+ /* reset baseband */
+ adm8211_write_bbp(dev, 0x00, 0xFF);
+ /* antenna selection: diversity */
+ adm8211_write_bbp(dev, 0x07, 0x0A);
+
+ /* TODO: find documentation for this */
+ switch (priv->transceiver_type) {
+ case ADM8211_RFMD2958:
+ case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
+ adm8211_write_bbp(dev, 0x00, 0x00);
+ adm8211_write_bbp(dev, 0x01, 0x00);
+ adm8211_write_bbp(dev, 0x02, 0x00);
+ adm8211_write_bbp(dev, 0x03, 0x00);
+ adm8211_write_bbp(dev, 0x06, 0x0f);
+ adm8211_write_bbp(dev, 0x09, 0x00);
+ adm8211_write_bbp(dev, 0x0a, 0x00);
+ adm8211_write_bbp(dev, 0x0b, 0x00);
+ adm8211_write_bbp(dev, 0x0c, 0x00);
+ adm8211_write_bbp(dev, 0x0f, 0xAA);
+ adm8211_write_bbp(dev, 0x10, 0x8c);
+ adm8211_write_bbp(dev, 0x11, 0x43);
+ adm8211_write_bbp(dev, 0x18, 0x40);
+ adm8211_write_bbp(dev, 0x20, 0x23);
+ adm8211_write_bbp(dev, 0x21, 0x02);
+ adm8211_write_bbp(dev, 0x22, 0x28);
+ adm8211_write_bbp(dev, 0x23, 0x30);
+ adm8211_write_bbp(dev, 0x24, 0x2d);
+ adm8211_write_bbp(dev, 0x28, 0x35);
+ adm8211_write_bbp(dev, 0x2a, 0x8c);
+ adm8211_write_bbp(dev, 0x2b, 0x81);
+ adm8211_write_bbp(dev, 0x2c, 0x44);
+ adm8211_write_bbp(dev, 0x2d, 0x0A);
+ adm8211_write_bbp(dev, 0x29, 0x40);
+ adm8211_write_bbp(dev, 0x60, 0x08);
+ adm8211_write_bbp(dev, 0x64, 0x01);
+ break;
+
+ case ADM8211_MAX2820:
+ adm8211_write_bbp(dev, 0x00, 0x00);
+ adm8211_write_bbp(dev, 0x01, 0x00);
+ adm8211_write_bbp(dev, 0x02, 0x00);
+ adm8211_write_bbp(dev, 0x03, 0x00);
+ adm8211_write_bbp(dev, 0x06, 0x0f);
+ adm8211_write_bbp(dev, 0x09, 0x05);
+ adm8211_write_bbp(dev, 0x0a, 0x02);
+ adm8211_write_bbp(dev, 0x0b, 0x00);
+ adm8211_write_bbp(dev, 0x0c, 0x0f);
+ adm8211_write_bbp(dev, 0x0f, 0x55);
+ adm8211_write_bbp(dev, 0x10, 0x8d);
+ adm8211_write_bbp(dev, 0x11, 0x43);
+ adm8211_write_bbp(dev, 0x18, 0x4a);
+ adm8211_write_bbp(dev, 0x20, 0x20);
+ adm8211_write_bbp(dev, 0x21, 0x02);
+ adm8211_write_bbp(dev, 0x22, 0x23);
+ adm8211_write_bbp(dev, 0x23, 0x30);
+ adm8211_write_bbp(dev, 0x24, 0x2d);
+ adm8211_write_bbp(dev, 0x2a, 0x8c);
+ adm8211_write_bbp(dev, 0x2b, 0x81);
+ adm8211_write_bbp(dev, 0x2c, 0x44);
+ adm8211_write_bbp(dev, 0x29, 0x4a);
+ adm8211_write_bbp(dev, 0x60, 0x2b);
+ adm8211_write_bbp(dev, 0x64, 0x01);
+ break;
+
+ case ADM8211_AL2210L:
+ adm8211_write_bbp(dev, 0x00, 0x00);
+ adm8211_write_bbp(dev, 0x01, 0x00);
+ adm8211_write_bbp(dev, 0x02, 0x00);
+ adm8211_write_bbp(dev, 0x03, 0x00);
+ adm8211_write_bbp(dev, 0x06, 0x0f);
+ adm8211_write_bbp(dev, 0x07, 0x05);
+ adm8211_write_bbp(dev, 0x08, 0x03);
+ adm8211_write_bbp(dev, 0x09, 0x00);
+ adm8211_write_bbp(dev, 0x0a, 0x00);
+ adm8211_write_bbp(dev, 0x0b, 0x00);
+ adm8211_write_bbp(dev, 0x0c, 0x10);
+ adm8211_write_bbp(dev, 0x0f, 0x55);
+ adm8211_write_bbp(dev, 0x10, 0x8d);
+ adm8211_write_bbp(dev, 0x11, 0x43);
+ adm8211_write_bbp(dev, 0x18, 0x4a);
+ adm8211_write_bbp(dev, 0x20, 0x20);
+ adm8211_write_bbp(dev, 0x21, 0x02);
+ adm8211_write_bbp(dev, 0x22, 0x23);
+ adm8211_write_bbp(dev, 0x23, 0x30);
+ adm8211_write_bbp(dev, 0x24, 0x2d);
+ adm8211_write_bbp(dev, 0x2a, 0xaa);
+ adm8211_write_bbp(dev, 0x2b, 0x81);
+ adm8211_write_bbp(dev, 0x2c, 0x44);
+ adm8211_write_bbp(dev, 0x29, 0xfa);
+ adm8211_write_bbp(dev, 0x60, 0x2d);
+ adm8211_write_bbp(dev, 0x64, 0x01);
+ break;
+
+ case ADM8211_RFMD2948:
+ break;
+
+ default:
+ wiphy_debug(dev->wiphy, "unsupported transceiver %d\n",
+ priv->transceiver_type);
+ break;
+ }
+ } else
+ wiphy_debug(dev->wiphy, "unsupported BBP %d\n", priv->bbp_type);
+
+ ADM8211_CSR_WRITE(SYNRF, 0);
+
+ /* Set RF CAL control source to MAC control */
+ reg = ADM8211_CSR_READ(SYNCTL);
+ reg |= ADM8211_SYNCTL_SELCAL;
+ ADM8211_CSR_WRITE(SYNCTL, reg);
+
+ return 0;
+}
+
+/* configures hw beacons/probe responses */
+static int adm8211_set_rate(struct ieee80211_hw *dev)
+{
+ struct adm8211_priv *priv = dev->priv;
+ u32 reg;
+ int i = 0;
+ u8 rate_buf[12] = {0};
+
+ /* write supported rates */
+ if (priv->pdev->revision != ADM8211_REV_BA) {
+ rate_buf[0] = ARRAY_SIZE(adm8211_rates);
+ for (i = 0; i < ARRAY_SIZE(adm8211_rates); i++)
+ rate_buf[i + 1] = (adm8211_rates[i].bitrate / 5) | 0x80;
+ } else {
+ /* workaround for rev BA specific bug */
+ rate_buf[0] = 0x04;
+ rate_buf[1] = 0x82;
+ rate_buf[2] = 0x04;
+ rate_buf[3] = 0x0b;
+ rate_buf[4] = 0x16;
+ }
+
+ adm8211_write_sram_bytes(dev, ADM8211_SRAM_SUPP_RATE, rate_buf,
+ ARRAY_SIZE(adm8211_rates) + 1);
+
+ reg = ADM8211_CSR_READ(PLCPHD) & 0x00FFFFFF; /* keep bits 0-23 */
+ reg |= 1 << 15; /* short preamble */
+ reg |= 110 << 24;
+ ADM8211_CSR_WRITE(PLCPHD, reg);
+
+ /* MTMLT = 512 TU (max TX MSDU lifetime)
+ * BCNTSIG = plcp_signal (beacon, probe resp, and atim TX rate)
+ * SRTYLIM = 224 (short retry limit, TX header value is default) */
+ ADM8211_CSR_WRITE(TXLMT, (512 << 16) | (110 << 8) | (224 << 0));
+
+ return 0;
+}
+
+static void adm8211_hw_init(struct ieee80211_hw *dev)
+{
+ struct adm8211_priv *priv = dev->priv;
+ u32 reg;
+ u8 cline;
+
+ reg = ADM8211_CSR_READ(PAR);
+ reg |= ADM8211_PAR_MRLE | ADM8211_PAR_MRME;
+ reg &= ~(ADM8211_PAR_BAR | ADM8211_PAR_CAL);
+
+ if (!pci_set_mwi(priv->pdev)) {
+ reg |= 0x1 << 24;
+ pci_read_config_byte(priv->pdev, PCI_CACHE_LINE_SIZE, &cline);
+
+ switch (cline) {
+ case 0x8: reg |= (0x1 << 14);
+ break;
+ case 0x16: reg |= (0x2 << 14);
+ break;
+ case 0x32: reg |= (0x3 << 14);
+ break;
+ default: reg |= (0x0 << 14);
+ break;
+ }
+ }
+
+ ADM8211_CSR_WRITE(PAR, reg);
+
+ reg = ADM8211_CSR_READ(CSR_TEST1);
+ reg &= ~(0xF << 28);
+ reg |= (1 << 28) | (1 << 31);
+ ADM8211_CSR_WRITE(CSR_TEST1, reg);
+
+ /* lose link after 4 lost beacons */
+ reg = (0x04 << 21) | ADM8211_WCSR_TSFTWE | ADM8211_WCSR_LSOE;
+ ADM8211_CSR_WRITE(WCSR, reg);
+
+ /* Disable APM, enable receive FIFO threshold, and set drain receive
+ * threshold to store-and-forward */
+ reg = ADM8211_CSR_READ(CMDR);
+ reg &= ~(ADM8211_CMDR_APM | ADM8211_CMDR_DRT);
+ reg |= ADM8211_CMDR_RTE | ADM8211_CMDR_DRT_SF;
+ ADM8211_CSR_WRITE(CMDR, reg);
+
+ adm8211_set_rate(dev);
+
+ /* 4-bit values:
+ * PWR1UP = 8 * 2 ms
+ * PWR0PAPE = 8 us or 5 us
+ * PWR1PAPE = 1 us or 3 us
+ * PWR0TRSW = 5 us
+ * PWR1TRSW = 12 us
+ * PWR0PE2 = 13 us
+ * PWR1PE2 = 1 us
+ * PWR0TXPE = 8 or 6 */
+ if (priv->pdev->revision < ADM8211_REV_CA)
+ ADM8211_CSR_WRITE(TOFS2, 0x8815cd18);
+ else
+ ADM8211_CSR_WRITE(TOFS2, 0x8535cd16);
+
+ /* Enable store and forward for transmit */
+ priv->nar = ADM8211_NAR_SF | ADM8211_NAR_PB;
+ ADM8211_CSR_WRITE(NAR, priv->nar);
+
+ /* Reset RF */
+ ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_RADIO);
+ ADM8211_CSR_READ(SYNRF);
+ msleep(10);
+ ADM8211_CSR_WRITE(SYNRF, 0);
+ ADM8211_CSR_READ(SYNRF);
+ msleep(5);
+
+ /* Set CFP Max Duration to 0x10 TU */
+ reg = ADM8211_CSR_READ(CFPP);
+ reg &= ~(0xffff << 8);
+ reg |= 0x0010 << 8;
+ ADM8211_CSR_WRITE(CFPP, reg);
+
+ /* USCNT = 0x16 (number of system clocks, 22 MHz, in 1us
+ * TUCNT = 0x3ff - Tu counter 1024 us */
+ ADM8211_CSR_WRITE(TOFS0, (0x16 << 24) | 0x3ff);
+
+ /* SLOT=20 us, SIFS=110 cycles of 22 MHz (5 us),
+ * DIFS=50 us, EIFS=100 us */
+ if (priv->pdev->revision < ADM8211_REV_CA)
+ ADM8211_CSR_WRITE(IFST, (20 << 23) | (110 << 15) |
+ (50 << 9) | 100);
+ else
+ ADM8211_CSR_WRITE(IFST, (20 << 23) | (24 << 15) |
+ (50 << 9) | 100);
+
+ /* PCNT = 1 (MAC idle time awake/sleep, unit S)
+ * RMRD = 2346 * 8 + 1 us (max RX duration) */
+ ADM8211_CSR_WRITE(RMD, (1 << 16) | 18769);
+
+ /* MART=65535 us, MIRT=256 us, TSFTOFST=0 us */
+ ADM8211_CSR_WRITE(RSPT, 0xffffff00);
+
+ /* Initialize BBP (and SYN) */
+ adm8211_hw_init_bbp(dev);
+
+ /* make sure interrupts are off */
+ ADM8211_CSR_WRITE(IER, 0);
+
+ /* ACK interrupts */
+ ADM8211_CSR_WRITE(STSR, ADM8211_CSR_READ(STSR));
+
+ /* Setup WEP (turns it off for now) */
+ reg = ADM8211_CSR_READ(MACTEST);
+ reg &= ~(7 << 20);
+ ADM8211_CSR_WRITE(MACTEST, reg);
+
+ reg = ADM8211_CSR_READ(WEPCTL);
+ reg &= ~ADM8211_WEPCTL_WEPENABLE;
+ reg |= ADM8211_WEPCTL_WEPRXBYP;
+ ADM8211_CSR_WRITE(WEPCTL, reg);
+
+ /* Clear the missed-packet counter. */
+ ADM8211_CSR_READ(LPC);
+}
+
+static int adm8211_hw_reset(struct ieee80211_hw *dev)
+{
+ struct adm8211_priv *priv = dev->priv;
+ u32 reg, tmp;
+ int timeout = 100;
+
+ /* Power-on issue */
+ /* TODO: check if this is necessary */
+ ADM8211_CSR_WRITE(FRCTL, 0);
+
+ /* Reset the chip */
+ tmp = ADM8211_CSR_READ(PAR);
+ ADM8211_CSR_WRITE(PAR, ADM8211_PAR_SWR);
+
+ while ((ADM8211_CSR_READ(PAR) & ADM8211_PAR_SWR) && timeout--)
+ msleep(50);
+
+ if (timeout <= 0)
+ return -ETIMEDOUT;
+
+ ADM8211_CSR_WRITE(PAR, tmp);
+
+ if (priv->pdev->revision == ADM8211_REV_BA &&
+ (priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER ||
+ priv->transceiver_type == ADM8211_RFMD2958)) {
+ reg = ADM8211_CSR_READ(CSR_TEST1);
+ reg |= (1 << 4) | (1 << 5);
+ ADM8211_CSR_WRITE(CSR_TEST1, reg);
+ } else if (priv->pdev->revision == ADM8211_REV_CA) {
+ reg = ADM8211_CSR_READ(CSR_TEST1);
+ reg &= ~((1 << 4) | (1 << 5));
+ ADM8211_CSR_WRITE(CSR_TEST1, reg);
+ }
+
+ ADM8211_CSR_WRITE(FRCTL, 0);
+
+ reg = ADM8211_CSR_READ(CSR_TEST0);
+ reg |= ADM8211_CSR_TEST0_EPRLD; /* EEPROM Recall */
+ ADM8211_CSR_WRITE(CSR_TEST0, reg);
+
+ adm8211_clear_sram(dev);
+
+ return 0;
+}
+
+static u64 adm8211_get_tsft(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
+{
+ struct adm8211_priv *priv = dev->priv;
+ u32 tsftl;
+ u64 tsft;
+
+ tsftl = ADM8211_CSR_READ(TSFTL);
+ tsft = ADM8211_CSR_READ(TSFTH);
+ tsft <<= 32;
+ tsft |= tsftl;
+
+ return tsft;
+}
+
+static void adm8211_set_interval(struct ieee80211_hw *dev,
+ unsigned short bi, unsigned short li)
+{
+ struct adm8211_priv *priv = dev->priv;
+ u32 reg;
+
+ /* BP (beacon interval) = data->beacon_interval
+ * LI (listen interval) = data->listen_interval (in beacon intervals) */
+ reg = (bi << 16) | li;
+ ADM8211_CSR_WRITE(BPLI, reg);
+}
+
+static void adm8211_set_bssid(struct ieee80211_hw *dev, const u8 *bssid)
+{
+ struct adm8211_priv *priv = dev->priv;
+ u32 reg;
+
+ ADM8211_CSR_WRITE(BSSID0, le32_to_cpu(*(__le32 *)bssid));
+ reg = ADM8211_CSR_READ(ABDA1);
+ reg &= 0x0000ffff;
+ reg |= (bssid[4] << 16) | (bssid[5] << 24);
+ ADM8211_CSR_WRITE(ABDA1, reg);
+}
+
+static int adm8211_config(struct ieee80211_hw *dev, u32 changed)
+{
+ struct adm8211_priv *priv = dev->priv;
+ struct ieee80211_conf *conf = &dev->conf;
+ int channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
+
+ if (channel != priv->channel) {
+ priv->channel = channel;
+ adm8211_rf_set_channel(dev, priv->channel);
+ }
+
+ return 0;
+}
+
+static void adm8211_bss_info_changed(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *conf,
+ u32 changes)
+{
+ struct adm8211_priv *priv = dev->priv;
+
+ if (!(changes & BSS_CHANGED_BSSID))
+ return;
+
+ if (memcmp(conf->bssid, priv->bssid, ETH_ALEN)) {
+ adm8211_set_bssid(dev, conf->bssid);
+ memcpy(priv->bssid, conf->bssid, ETH_ALEN);
+ }
+}
+
+static u64 adm8211_prepare_multicast(struct ieee80211_hw *hw,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ struct netdev_hw_addr_list *mc_list)
+#else
+ int mc_count, struct dev_addr_list *ha)
+#endif
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ unsigned int bit_nr;
+ struct netdev_hw_addr *ha;
+#else
+ unsigned int bit_nr, i;
+#endif
+ u32 mc_filter[2];
+
+ mc_filter[1] = mc_filter[0] = 0;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ netdev_hw_addr_list_for_each(ha, mc_list) {
+ bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26;
+#else
+ for (i = 0; i < mc_count; i++) {
+ if (!ha)
+ break;
+ bit_nr = ether_crc(ETH_ALEN, ha->dmi_addr) >> 26;
+#endif
+
+ bit_nr &= 0x3F;
+ mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35))
+ ha = ha->next;
+#endif
+ }
+
+ return mc_filter[0] | ((u64)(mc_filter[1]) << 32);
+}
+
+static void adm8211_configure_filter(struct ieee80211_hw *dev,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast)
+{
+ static const u8 bcast[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+ struct adm8211_priv *priv = dev->priv;
+ unsigned int new_flags;
+ u32 mc_filter[2];
+
+ mc_filter[0] = multicast;
+ mc_filter[1] = multicast >> 32;
+
+ new_flags = 0;
+
+ if (*total_flags & FIF_PROMISC_IN_BSS) {
+ new_flags |= FIF_PROMISC_IN_BSS;
+ priv->nar |= ADM8211_NAR_PR;
+ priv->nar &= ~ADM8211_NAR_MM;
+ mc_filter[1] = mc_filter[0] = ~0;
+ } else if (*total_flags & FIF_ALLMULTI || multicast == ~(0ULL)) {
+ new_flags |= FIF_ALLMULTI;
+ priv->nar &= ~ADM8211_NAR_PR;
+ priv->nar |= ADM8211_NAR_MM;
+ mc_filter[1] = mc_filter[0] = ~0;
+ } else {
+ priv->nar &= ~(ADM8211_NAR_MM | ADM8211_NAR_PR);
+ }
+
+ ADM8211_IDLE_RX();
+
+ ADM8211_CSR_WRITE(MAR0, mc_filter[0]);
+ ADM8211_CSR_WRITE(MAR1, mc_filter[1]);
+ ADM8211_CSR_READ(NAR);
+
+ if (priv->nar & ADM8211_NAR_PR)
+ dev->flags |= IEEE80211_HW_RX_INCLUDES_FCS;
+ else
+ dev->flags &= ~IEEE80211_HW_RX_INCLUDES_FCS;
+
+ if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
+ adm8211_set_bssid(dev, bcast);
+ else
+ adm8211_set_bssid(dev, priv->bssid);
+
+ ADM8211_RESTORE();
+
+ *total_flags = new_flags;
+}
+
+static int adm8211_add_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
+{
+ struct adm8211_priv *priv = dev->priv;
+ if (priv->mode != NL80211_IFTYPE_MONITOR)
+ return -EOPNOTSUPP;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ priv->mode = vif->type;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ ADM8211_IDLE();
+
+ ADM8211_CSR_WRITE(PAR0, le32_to_cpu(*(__le32 *)vif->addr));
+ ADM8211_CSR_WRITE(PAR1, le16_to_cpu(*(__le16 *)(vif->addr + 4)));
+
+ adm8211_update_mode(dev);
+
+ ADM8211_RESTORE();
+
+ return 0;
+}
+
+static void adm8211_remove_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
+{
+ struct adm8211_priv *priv = dev->priv;
+ priv->mode = NL80211_IFTYPE_MONITOR;
+}
+
+static int adm8211_init_rings(struct ieee80211_hw *dev)
+{
+ struct adm8211_priv *priv = dev->priv;
+ struct adm8211_desc *desc = NULL;
+ struct adm8211_rx_ring_info *rx_info;
+ struct adm8211_tx_ring_info *tx_info;
+ unsigned int i;
+
+ for (i = 0; i < priv->rx_ring_size; i++) {
+ desc = &priv->rx_ring[i];
+ desc->status = 0;
+ desc->length = cpu_to_le32(RX_PKT_SIZE);
+ priv->rx_buffers[i].skb = NULL;
+ }
+ /* Mark the end of RX ring; hw returns to base address after this
+ * descriptor */
+ desc->length |= cpu_to_le32(RDES1_CONTROL_RER);
+
+ for (i = 0; i < priv->rx_ring_size; i++) {
+ desc = &priv->rx_ring[i];
+ rx_info = &priv->rx_buffers[i];
+
+ rx_info->skb = dev_alloc_skb(RX_PKT_SIZE);
+ if (rx_info->skb == NULL)
+ break;
+ rx_info->mapping = pci_map_single(priv->pdev,
+ skb_tail_pointer(rx_info->skb),
+ RX_PKT_SIZE,
+ PCI_DMA_FROMDEVICE);
+ desc->buffer1 = cpu_to_le32(rx_info->mapping);
+ desc->status = cpu_to_le32(RDES0_STATUS_OWN | RDES0_STATUS_SQL);
+ }
+
+ /* Setup TX ring. TX buffers descriptors will be filled in as needed */
+ for (i = 0; i < priv->tx_ring_size; i++) {
+ desc = &priv->tx_ring[i];
+ tx_info = &priv->tx_buffers[i];
+
+ tx_info->skb = NULL;
+ tx_info->mapping = 0;
+ desc->status = 0;
+ }
+ desc->length = cpu_to_le32(TDES1_CONTROL_TER);
+
+ priv->cur_rx = priv->cur_tx = priv->dirty_tx = 0;
+ ADM8211_CSR_WRITE(RDB, priv->rx_ring_dma);
+ ADM8211_CSR_WRITE(TDBD, priv->tx_ring_dma);
+
+ return 0;
+}
+
+static void adm8211_free_rings(struct ieee80211_hw *dev)
+{
+ struct adm8211_priv *priv = dev->priv;
+ unsigned int i;
+
+ for (i = 0; i < priv->rx_ring_size; i++) {
+ if (!priv->rx_buffers[i].skb)
+ continue;
+
+ pci_unmap_single(
+ priv->pdev,
+ priv->rx_buffers[i].mapping,
+ RX_PKT_SIZE, PCI_DMA_FROMDEVICE);
+
+ dev_kfree_skb(priv->rx_buffers[i].skb);
+ }
+
+ for (i = 0; i < priv->tx_ring_size; i++) {
+ if (!priv->tx_buffers[i].skb)
+ continue;
+
+ pci_unmap_single(priv->pdev,
+ priv->tx_buffers[i].mapping,
+ priv->tx_buffers[i].skb->len,
+ PCI_DMA_TODEVICE);
+
+ dev_kfree_skb(priv->tx_buffers[i].skb);
+ }
+}
+
+static int adm8211_start(struct ieee80211_hw *dev)
+{
+ struct adm8211_priv *priv = dev->priv;
+ int retval;
+
+ /* Power up MAC and RF chips */
+ retval = adm8211_hw_reset(dev);
+ if (retval) {
+ wiphy_err(dev->wiphy, "hardware reset failed\n");
+ goto fail;
+ }
+
+ retval = adm8211_init_rings(dev);
+ if (retval) {
+ wiphy_err(dev->wiphy, "failed to initialize rings\n");
+ goto fail;
+ }
+
+ /* Init hardware */
+ adm8211_hw_init(dev);
+ adm8211_rf_set_channel(dev, priv->channel);
+
+ retval = request_irq(priv->pdev->irq, adm8211_interrupt,
+ IRQF_SHARED, "adm8211", dev);
+ if (retval) {
+ wiphy_err(dev->wiphy, "failed to register IRQ handler\n");
+ goto fail;
+ }
+
+ ADM8211_CSR_WRITE(IER, ADM8211_IER_NIE | ADM8211_IER_AIE |
+ ADM8211_IER_RCIE | ADM8211_IER_TCIE |
+ ADM8211_IER_TDUIE | ADM8211_IER_GPTIE);
+ priv->mode = NL80211_IFTYPE_MONITOR;
+ adm8211_update_mode(dev);
+ ADM8211_CSR_WRITE(RDR, 0);
+
+ adm8211_set_interval(dev, 100, 10);
+ return 0;
+
+fail:
+ return retval;
+}
+
+static void adm8211_stop(struct ieee80211_hw *dev)
+{
+ struct adm8211_priv *priv = dev->priv;
+
+ priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->nar = 0;
+ ADM8211_CSR_WRITE(NAR, 0);
+ ADM8211_CSR_WRITE(IER, 0);
+ ADM8211_CSR_READ(NAR);
+
+ free_irq(priv->pdev->irq, dev);
+
+ adm8211_free_rings(dev);
+}
+
+static void adm8211_calc_durations(int *dur, int *plcp, size_t payload_len, int len,
+ int plcp_signal, int short_preamble)
+{
+ /* Alternative calculation from NetBSD: */
+
+/* IEEE 802.11b durations for DSSS PHY in microseconds */
+#define IEEE80211_DUR_DS_LONG_PREAMBLE 144
+#define IEEE80211_DUR_DS_SHORT_PREAMBLE 72
+#define IEEE80211_DUR_DS_FAST_PLCPHDR 24
+#define IEEE80211_DUR_DS_SLOW_PLCPHDR 48
+#define IEEE80211_DUR_DS_SLOW_ACK 112
+#define IEEE80211_DUR_DS_FAST_ACK 56
+#define IEEE80211_DUR_DS_SLOW_CTS 112
+#define IEEE80211_DUR_DS_FAST_CTS 56
+#define IEEE80211_DUR_DS_SLOT 20
+#define IEEE80211_DUR_DS_SIFS 10
+
+ int remainder;
+
+ *dur = (80 * (24 + payload_len) + plcp_signal - 1)
+ / plcp_signal;
+
+ if (plcp_signal <= PLCP_SIGNAL_2M)
+ /* 1-2Mbps WLAN: send ACK/CTS at 1Mbps */
+ *dur += 3 * (IEEE80211_DUR_DS_SIFS +
+ IEEE80211_DUR_DS_SHORT_PREAMBLE +
+ IEEE80211_DUR_DS_FAST_PLCPHDR) +
+ IEEE80211_DUR_DS_SLOW_CTS + IEEE80211_DUR_DS_SLOW_ACK;
+ else
+ /* 5-11Mbps WLAN: send ACK/CTS at 2Mbps */
+ *dur += 3 * (IEEE80211_DUR_DS_SIFS +
+ IEEE80211_DUR_DS_SHORT_PREAMBLE +
+ IEEE80211_DUR_DS_FAST_PLCPHDR) +
+ IEEE80211_DUR_DS_FAST_CTS + IEEE80211_DUR_DS_FAST_ACK;
+
+ /* lengthen duration if long preamble */
+ if (!short_preamble)
+ *dur += 3 * (IEEE80211_DUR_DS_LONG_PREAMBLE -
+ IEEE80211_DUR_DS_SHORT_PREAMBLE) +
+ 3 * (IEEE80211_DUR_DS_SLOW_PLCPHDR -
+ IEEE80211_DUR_DS_FAST_PLCPHDR);
+
+
+ *plcp = (80 * len) / plcp_signal;
+ remainder = (80 * len) % plcp_signal;
+ if (plcp_signal == PLCP_SIGNAL_11M &&
+ remainder <= 30 && remainder > 0)
+ *plcp = (*plcp | 0x8000) + 1;
+ else if (remainder)
+ (*plcp)++;
+}
+
+/* Transmit skb w/adm8211_tx_hdr (802.11 header created by hardware) */
+static void adm8211_tx_raw(struct ieee80211_hw *dev, struct sk_buff *skb,
+ u16 plcp_signal,
+ size_t hdrlen)
+{
+ struct adm8211_priv *priv = dev->priv;
+ unsigned long flags;
+ dma_addr_t mapping;
+ unsigned int entry;
+ u32 flag;
+
+ mapping = pci_map_single(priv->pdev, skb->data, skb->len,
+ PCI_DMA_TODEVICE);
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (priv->cur_tx - priv->dirty_tx == priv->tx_ring_size / 2)
+ flag = TDES1_CONTROL_IC | TDES1_CONTROL_LS | TDES1_CONTROL_FS;
+ else
+ flag = TDES1_CONTROL_LS | TDES1_CONTROL_FS;
+
+ if (priv->cur_tx - priv->dirty_tx == priv->tx_ring_size - 2)
+ ieee80211_stop_queue(dev, 0);
+
+ entry = priv->cur_tx % priv->tx_ring_size;
+
+ priv->tx_buffers[entry].skb = skb;
+ priv->tx_buffers[entry].mapping = mapping;
+ priv->tx_buffers[entry].hdrlen = hdrlen;
+ priv->tx_ring[entry].buffer1 = cpu_to_le32(mapping);
+
+ if (entry == priv->tx_ring_size - 1)
+ flag |= TDES1_CONTROL_TER;
+ priv->tx_ring[entry].length = cpu_to_le32(flag | skb->len);
+
+ /* Set TX rate (SIGNAL field in PLCP PPDU format) */
+ flag = TDES0_CONTROL_OWN | (plcp_signal << 20) | 8 /* ? */;
+ priv->tx_ring[entry].status = cpu_to_le32(flag);
+
+ priv->cur_tx++;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ /* Trigger transmit poll */
+ ADM8211_CSR_WRITE(TDR, 0);
+}
+
+/* Put adm8211_tx_hdr on skb and transmit */
+static void adm8211_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
+{
+ struct adm8211_tx_hdr *txhdr;
+ size_t payload_len, hdrlen;
+ int plcp, dur, len, plcp_signal, short_preamble;
+ struct ieee80211_hdr *hdr;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_rate *txrate = ieee80211_get_tx_rate(dev, info);
+ u8 rc_flags;
+
+ rc_flags = info->control.rates[0].flags;
+ short_preamble = !!(rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
+ plcp_signal = txrate->bitrate;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ memcpy(skb->cb, skb->data, hdrlen);
+ hdr = (struct ieee80211_hdr *)skb->cb;
+ skb_pull(skb, hdrlen);
+ payload_len = skb->len;
+
+ txhdr = (struct adm8211_tx_hdr *) skb_push(skb, sizeof(*txhdr));
+ memset(txhdr, 0, sizeof(*txhdr));
+ memcpy(txhdr->da, ieee80211_get_DA(hdr), ETH_ALEN);
+ txhdr->signal = plcp_signal;
+ txhdr->frame_body_size = cpu_to_le16(payload_len);
+ txhdr->frame_control = hdr->frame_control;
+
+ len = hdrlen + payload_len + FCS_LEN;
+
+ txhdr->frag = cpu_to_le16(0x0FFF);
+ adm8211_calc_durations(&dur, &plcp, payload_len,
+ len, plcp_signal, short_preamble);
+ txhdr->plcp_frag_head_len = cpu_to_le16(plcp);
+ txhdr->plcp_frag_tail_len = cpu_to_le16(plcp);
+ txhdr->dur_frag_head = cpu_to_le16(dur);
+ txhdr->dur_frag_tail = cpu_to_le16(dur);
+
+ txhdr->header_control = cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER);
+
+ if (short_preamble)
+ txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_SHORT_PREAMBLE);
+
+ if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS)
+ txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_RTS);
+
+ txhdr->retry_limit = info->control.rates[0].count;
+
+ adm8211_tx_raw(dev, skb, plcp_signal, hdrlen);
+}
+
+static int adm8211_alloc_rings(struct ieee80211_hw *dev)
+{
+ struct adm8211_priv *priv = dev->priv;
+ unsigned int ring_size;
+
+ priv->rx_buffers = kmalloc(sizeof(*priv->rx_buffers) * priv->rx_ring_size +
+ sizeof(*priv->tx_buffers) * priv->tx_ring_size, GFP_KERNEL);
+ if (!priv->rx_buffers)
+ return -ENOMEM;
+
+ priv->tx_buffers = (void *)priv->rx_buffers +
+ sizeof(*priv->rx_buffers) * priv->rx_ring_size;
+
+ /* Allocate TX/RX descriptors */
+ ring_size = sizeof(struct adm8211_desc) * priv->rx_ring_size +
+ sizeof(struct adm8211_desc) * priv->tx_ring_size;
+ priv->rx_ring = pci_alloc_consistent(priv->pdev, ring_size,
+ &priv->rx_ring_dma);
+
+ if (!priv->rx_ring) {
+ kfree(priv->rx_buffers);
+ priv->rx_buffers = NULL;
+ priv->tx_buffers = NULL;
+ return -ENOMEM;
+ }
+
+ priv->tx_ring = (struct adm8211_desc *)(priv->rx_ring +
+ priv->rx_ring_size);
+ priv->tx_ring_dma = priv->rx_ring_dma +
+ sizeof(struct adm8211_desc) * priv->rx_ring_size;
+
+ return 0;
+}
+
+static const struct ieee80211_ops adm8211_ops = {
+ .tx = adm8211_tx,
+ .start = adm8211_start,
+ .stop = adm8211_stop,
+ .add_interface = adm8211_add_interface,
+ .remove_interface = adm8211_remove_interface,
+ .config = adm8211_config,
+ .bss_info_changed = adm8211_bss_info_changed,
+ .prepare_multicast = adm8211_prepare_multicast,
+ .configure_filter = adm8211_configure_filter,
+ .get_stats = adm8211_get_stats,
+ .get_tsf = adm8211_get_tsft
+};
+
+static int __devinit adm8211_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct ieee80211_hw *dev;
+ struct adm8211_priv *priv;
+ unsigned long mem_addr, mem_len;
+ unsigned int io_addr, io_len;
+ int err;
+ u32 reg;
+ u8 perm_addr[ETH_ALEN];
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ printk(KERN_ERR "%s (adm8211): Cannot enable new PCI device\n",
+ pci_name(pdev));
+ return err;
+ }
+
+ io_addr = pci_resource_start(pdev, 0);
+ io_len = pci_resource_len(pdev, 0);
+ mem_addr = pci_resource_start(pdev, 1);
+ mem_len = pci_resource_len(pdev, 1);
+ if (io_len < 256 || mem_len < 1024) {
+ printk(KERN_ERR "%s (adm8211): Too short PCI resources\n",
+ pci_name(pdev));
+ goto err_disable_pdev;
+ }
+
+
+ /* check signature */
+ pci_read_config_dword(pdev, 0x80 /* CR32 */, ®);
+ if (reg != ADM8211_SIG1 && reg != ADM8211_SIG2) {
+ printk(KERN_ERR "%s (adm8211): Invalid signature (0x%x)\n",
+ pci_name(pdev), reg);
+ goto err_disable_pdev;
+ }
+
+ err = pci_request_regions(pdev, "adm8211");
+ if (err) {
+ printk(KERN_ERR "%s (adm8211): Cannot obtain PCI resources\n",
+ pci_name(pdev));
+ return err; /* someone else grabbed it? don't disable it */
+ }
+
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) ||
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ printk(KERN_ERR "%s (adm8211): No suitable DMA available\n",
+ pci_name(pdev));
+ goto err_free_reg;
+ }
+
+ pci_set_master(pdev);
+
+ dev = ieee80211_alloc_hw(sizeof(*priv), &adm8211_ops);
+ if (!dev) {
+ printk(KERN_ERR "%s (adm8211): ieee80211 alloc failed\n",
+ pci_name(pdev));
+ err = -ENOMEM;
+ goto err_free_reg;
+ }
+ priv = dev->priv;
+ priv->pdev = pdev;
+
+ spin_lock_init(&priv->lock);
+
+ SET_IEEE80211_DEV(dev, &pdev->dev);
+
+ pci_set_drvdata(pdev, dev);
+
+ priv->map = pci_iomap(pdev, 1, mem_len);
+ if (!priv->map)
+ priv->map = pci_iomap(pdev, 0, io_len);
+
+ if (!priv->map) {
+ printk(KERN_ERR "%s (adm8211): Cannot map device memory\n",
+ pci_name(pdev));
+ goto err_free_dev;
+ }
+
+ priv->rx_ring_size = rx_ring_size;
+ priv->tx_ring_size = tx_ring_size;
+
+ if (adm8211_alloc_rings(dev)) {
+ printk(KERN_ERR "%s (adm8211): Cannot allocate TX/RX ring\n",
+ pci_name(pdev));
+ goto err_iounmap;
+ }
+
+ *(__le32 *)perm_addr = cpu_to_le32(ADM8211_CSR_READ(PAR0));
+ *(__le16 *)&perm_addr[4] =
+ cpu_to_le16(ADM8211_CSR_READ(PAR1) & 0xFFFF);
+
+ if (!is_valid_ether_addr(perm_addr)) {
+ printk(KERN_WARNING "%s (adm8211): Invalid hwaddr in EEPROM!\n",
+ pci_name(pdev));
+ random_ether_addr(perm_addr);
+ }
+ SET_IEEE80211_PERM_ADDR(dev, perm_addr);
+
+ dev->extra_tx_headroom = sizeof(struct adm8211_tx_hdr);
+ /* dev->flags = IEEE80211_HW_RX_INCLUDES_FCS in promisc mode */
+ dev->flags = IEEE80211_HW_SIGNAL_UNSPEC;
+ dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+
+ dev->channel_change_time = 1000;
+ dev->max_signal = 100; /* FIXME: find better value */
+
+ dev->queues = 1; /* ADM8211C supports more, maybe ADM8211B too */
+
+ priv->retry_limit = 3;
+ priv->ant_power = 0x40;
+ priv->tx_power = 0x40;
+ priv->lpf_cutoff = 0xFF;
+ priv->lnags_threshold = 0xFF;
+ priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+
+ /* Power-on issue. EEPROM won't read correctly without */
+ if (pdev->revision >= ADM8211_REV_BA) {
+ ADM8211_CSR_WRITE(FRCTL, 0);
+ ADM8211_CSR_READ(FRCTL);
+ ADM8211_CSR_WRITE(FRCTL, 1);
+ ADM8211_CSR_READ(FRCTL);
+ msleep(100);
+ }
+
+ err = adm8211_read_eeprom(dev);
+ if (err) {
+ printk(KERN_ERR "%s (adm8211): Can't alloc eeprom buffer\n",
+ pci_name(pdev));
+ goto err_free_desc;
+ }
+
+ priv->channel = 1;
+
+ dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
+
+ err = ieee80211_register_hw(dev);
+ if (err) {
+ printk(KERN_ERR "%s (adm8211): Cannot register device\n",
+ pci_name(pdev));
+ goto err_free_eeprom;
+ }
+
+ wiphy_info(dev->wiphy, "hwaddr %pM, Rev 0x%02x\n",
+ dev->wiphy->perm_addr, pdev->revision);
+
+ return 0;
+
+ err_free_eeprom:
+ kfree(priv->eeprom);
+
+ err_free_desc:
+ pci_free_consistent(pdev,
+ sizeof(struct adm8211_desc) * priv->rx_ring_size +
+ sizeof(struct adm8211_desc) * priv->tx_ring_size,
+ priv->rx_ring, priv->rx_ring_dma);
+ kfree(priv->rx_buffers);
+
+ err_iounmap:
+ pci_iounmap(pdev, priv->map);
+
+ err_free_dev:
+ pci_set_drvdata(pdev, NULL);
+ ieee80211_free_hw(dev);
+
+ err_free_reg:
+ pci_release_regions(pdev);
+
+ err_disable_pdev:
+ pci_disable_device(pdev);
+ return err;
+}
+
+
+static void __devexit adm8211_remove(struct pci_dev *pdev)
+{
+ struct ieee80211_hw *dev = pci_get_drvdata(pdev);
+ struct adm8211_priv *priv;
+
+ if (!dev)
+ return;
+
+ ieee80211_unregister_hw(dev);
+
+ priv = dev->priv;
+
+ pci_free_consistent(pdev,
+ sizeof(struct adm8211_desc) * priv->rx_ring_size +
+ sizeof(struct adm8211_desc) * priv->tx_ring_size,
+ priv->rx_ring, priv->rx_ring_dma);
+
+ kfree(priv->rx_buffers);
+ kfree(priv->eeprom);
+ pci_iounmap(pdev, priv->map);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ ieee80211_free_hw(dev);
+}
+
+
+#ifdef CONFIG_PM
+static int adm8211_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ pci_save_state(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ return 0;
+}
+
+static int adm8211_resume(struct pci_dev *pdev)
+{
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+
+MODULE_DEVICE_TABLE(pci, adm8211_pci_id_table);
+
+/* TODO: implement enable_wake */
+static struct pci_driver adm8211_driver = {
+ .name = "adm8211",
+ .id_table = adm8211_pci_id_table,
+ .probe = adm8211_probe,
+ .remove = __devexit_p(adm8211_remove),
+#ifdef CONFIG_PM
+ .suspend = adm8211_suspend,
+ .resume = adm8211_resume,
+#endif /* CONFIG_PM */
+};
+
+
+
+static int __init adm8211_init(void)
+{
+ return pci_register_driver(&adm8211_driver);
+}
+
+
+static void __exit adm8211_exit(void)
+{
+ pci_unregister_driver(&adm8211_driver);
+}
+
+
+module_init(adm8211_init);
+module_exit(adm8211_exit);
diff --git a/drivers/net/wireless/adm8211.h b/drivers/net/wireless/adm8211.h
new file mode 100644
index 0000000..bbc10b1
--- /dev/null
+++ b/drivers/net/wireless/adm8211.h
@@ -0,0 +1,602 @@
+#ifndef ADM8211_H
+#define ADM8211_H
+
+/* ADM8211 Registers */
+
+/* CR32 (SIG) signature */
+#define ADM8211_SIG1 0x82011317 /* ADM8211A */
+#define ADM8211_SIG2 0x82111317 /* ADM8211B/ADM8211C */
+
+#define ADM8211_CSR_READ(r) ioread32(&priv->map->r)
+#define ADM8211_CSR_WRITE(r, val) iowrite32((val), &priv->map->r)
+
+/* CSR (Host Control and Status Registers) */
+struct adm8211_csr {
+ __le32 PAR; /* 0x00 CSR0 */
+ __le32 FRCTL; /* 0x04 CSR0A */
+ __le32 TDR; /* 0x08 CSR1 */
+ __le32 WTDP; /* 0x0C CSR1A */
+ __le32 RDR; /* 0x10 CSR2 */
+ __le32 WRDP; /* 0x14 CSR2A */
+ __le32 RDB; /* 0x18 CSR3 */
+ __le32 TDBH; /* 0x1C CSR3A */
+ __le32 TDBD; /* 0x20 CSR4 */
+ __le32 TDBP; /* 0x24 CSR4A */
+ __le32 STSR; /* 0x28 CSR5 */
+ __le32 TDBB; /* 0x2C CSR5A */
+ __le32 NAR; /* 0x30 CSR6 */
+ __le32 CSR6A; /* reserved */
+ __le32 IER; /* 0x38 CSR7 */
+ __le32 TKIPSCEP; /* 0x3C CSR7A */
+ __le32 LPC; /* 0x40 CSR8 */
+ __le32 CSR_TEST1; /* 0x44 CSR8A */
+ __le32 SPR; /* 0x48 CSR9 */
+ __le32 CSR_TEST0; /* 0x4C CSR9A */
+ __le32 WCSR; /* 0x50 CSR10 */
+ __le32 WPDR; /* 0x54 CSR10A */
+ __le32 GPTMR; /* 0x58 CSR11 */
+ __le32 GPIO; /* 0x5C CSR11A */
+ __le32 BBPCTL; /* 0x60 CSR12 */
+ __le32 SYNCTL; /* 0x64 CSR12A */
+ __le32 PLCPHD; /* 0x68 CSR13 */
+ __le32 MMIWA; /* 0x6C CSR13A */
+ __le32 MMIRD0; /* 0x70 CSR14 */
+ __le32 MMIRD1; /* 0x74 CSR14A */
+ __le32 TXBR; /* 0x78 CSR15 */
+ __le32 SYNDATA; /* 0x7C CSR15A */
+ __le32 ALCS; /* 0x80 CSR16 */
+ __le32 TOFS2; /* 0x84 CSR17 */
+ __le32 CMDR; /* 0x88 CSR18 */
+ __le32 PCIC; /* 0x8C CSR19 */
+ __le32 PMCSR; /* 0x90 CSR20 */
+ __le32 PAR0; /* 0x94 CSR21 */
+ __le32 PAR1; /* 0x98 CSR22 */
+ __le32 MAR0; /* 0x9C CSR23 */
+ __le32 MAR1; /* 0xA0 CSR24 */
+ __le32 ATIMDA0; /* 0xA4 CSR25 */
+ __le32 ABDA1; /* 0xA8 CSR26 */
+ __le32 BSSID0; /* 0xAC CSR27 */
+ __le32 TXLMT; /* 0xB0 CSR28 */
+ __le32 MIBCNT; /* 0xB4 CSR29 */
+ __le32 BCNT; /* 0xB8 CSR30 */
+ __le32 TSFTH; /* 0xBC CSR31 */
+ __le32 TSC; /* 0xC0 CSR32 */
+ __le32 SYNRF; /* 0xC4 CSR33 */
+ __le32 BPLI; /* 0xC8 CSR34 */
+ __le32 CAP0; /* 0xCC CSR35 */
+ __le32 CAP1; /* 0xD0 CSR36 */
+ __le32 RMD; /* 0xD4 CSR37 */
+ __le32 CFPP; /* 0xD8 CSR38 */
+ __le32 TOFS0; /* 0xDC CSR39 */
+ __le32 TOFS1; /* 0xE0 CSR40 */
+ __le32 IFST; /* 0xE4 CSR41 */
+ __le32 RSPT; /* 0xE8 CSR42 */
+ __le32 TSFTL; /* 0xEC CSR43 */
+ __le32 WEPCTL; /* 0xF0 CSR44 */
+ __le32 WESK; /* 0xF4 CSR45 */
+ __le32 WEPCNT; /* 0xF8 CSR46 */
+ __le32 MACTEST; /* 0xFC CSR47 */
+ __le32 FER; /* 0x100 */
+ __le32 FEMR; /* 0x104 */
+ __le32 FPSR; /* 0x108 */
+ __le32 FFER; /* 0x10C */
+} __packed;
+
+/* CSR0 - PAR (PCI Address Register) */
+#define ADM8211_PAR_MWIE (1 << 24)
+#define ADM8211_PAR_MRLE (1 << 23)
+#define ADM8211_PAR_MRME (1 << 21)
+#define ADM8211_PAR_RAP ((1 << 18) | (1 << 17))
+#define ADM8211_PAR_CAL ((1 << 15) | (1 << 14))
+#define ADM8211_PAR_PBL 0x00003f00
+#define ADM8211_PAR_BLE (1 << 7)
+#define ADM8211_PAR_DSL 0x0000007c
+#define ADM8211_PAR_BAR (1 << 1)
+#define ADM8211_PAR_SWR (1 << 0)
+
+/* CSR1 - FRCTL (Frame Control Register) */
+#define ADM8211_FRCTL_PWRMGT (1 << 31)
+#define ADM8211_FRCTL_MAXPSP (1 << 27)
+#define ADM8211_FRCTL_DRVPRSP (1 << 26)
+#define ADM8211_FRCTL_DRVBCON (1 << 25)
+#define ADM8211_FRCTL_AID 0x0000ffff
+#define ADM8211_FRCTL_AID_ON 0x0000c000
+
+/* CSR5 - STSR (Status Register) */
+#define ADM8211_STSR_PCF (1 << 31)
+#define ADM8211_STSR_BCNTC (1 << 30)
+#define ADM8211_STSR_GPINT (1 << 29)
+#define ADM8211_STSR_LinkOff (1 << 28)
+#define ADM8211_STSR_ATIMTC (1 << 27)
+#define ADM8211_STSR_TSFTF (1 << 26)
+#define ADM8211_STSR_TSCZ (1 << 25)
+#define ADM8211_STSR_LinkOn (1 << 24)
+#define ADM8211_STSR_SQL (1 << 23)
+#define ADM8211_STSR_WEPTD (1 << 22)
+#define ADM8211_STSR_ATIME (1 << 21)
+#define ADM8211_STSR_TBTT (1 << 20)
+#define ADM8211_STSR_NISS (1 << 16)
+#define ADM8211_STSR_AISS (1 << 15)
+#define ADM8211_STSR_TEIS (1 << 14)
+#define ADM8211_STSR_FBE (1 << 13)
+#define ADM8211_STSR_REIS (1 << 12)
+#define ADM8211_STSR_GPTT (1 << 11)
+#define ADM8211_STSR_RPS (1 << 8)
+#define ADM8211_STSR_RDU (1 << 7)
+#define ADM8211_STSR_RCI (1 << 6)
+#define ADM8211_STSR_TUF (1 << 5)
+#define ADM8211_STSR_TRT (1 << 4)
+#define ADM8211_STSR_TLT (1 << 3)
+#define ADM8211_STSR_TDU (1 << 2)
+#define ADM8211_STSR_TPS (1 << 1)
+#define ADM8211_STSR_TCI (1 << 0)
+
+/* CSR6 - NAR (Network Access Register) */
+#define ADM8211_NAR_TXCF (1 << 31)
+#define ADM8211_NAR_HF (1 << 30)
+#define ADM8211_NAR_UTR (1 << 29)
+#define ADM8211_NAR_SQ (1 << 28)
+#define ADM8211_NAR_CFP (1 << 27)
+#define ADM8211_NAR_SF (1 << 21)
+#define ADM8211_NAR_TR ((1 << 15) | (1 << 14))
+#define ADM8211_NAR_ST (1 << 13)
+#define ADM8211_NAR_OM ((1 << 11) | (1 << 10))
+#define ADM8211_NAR_MM (1 << 7)
+#define ADM8211_NAR_PR (1 << 6)
+#define ADM8211_NAR_EA (1 << 5)
+#define ADM8211_NAR_PB (1 << 3)
+#define ADM8211_NAR_STPDMA (1 << 2)
+#define ADM8211_NAR_SR (1 << 1)
+#define ADM8211_NAR_CTX (1 << 0)
+
+#define ADM8211_IDLE() \
+do { \
+ if (priv->nar & (ADM8211_NAR_SR | ADM8211_NAR_ST)) { \
+ ADM8211_CSR_WRITE(NAR, priv->nar & \
+ ~(ADM8211_NAR_SR | ADM8211_NAR_ST));\
+ ADM8211_CSR_READ(NAR); \
+ msleep(20); \
+ } \
+} while (0)
+
+#define ADM8211_IDLE_RX() \
+do { \
+ if (priv->nar & ADM8211_NAR_SR) { \
+ ADM8211_CSR_WRITE(NAR, priv->nar & ~ADM8211_NAR_SR); \
+ ADM8211_CSR_READ(NAR); \
+ mdelay(20); \
+ } \
+} while (0)
+
+#define ADM8211_RESTORE() \
+do { \
+ if (priv->nar & (ADM8211_NAR_SR | ADM8211_NAR_ST)) \
+ ADM8211_CSR_WRITE(NAR, priv->nar); \
+} while (0)
+
+/* CSR7 - IER (Interrupt Enable Register) */
+#define ADM8211_IER_PCFIE (1 << 31)
+#define ADM8211_IER_BCNTCIE (1 << 30)
+#define ADM8211_IER_GPIE (1 << 29)
+#define ADM8211_IER_LinkOffIE (1 << 28)
+#define ADM8211_IER_ATIMTCIE (1 << 27)
+#define ADM8211_IER_TSFTFIE (1 << 26)
+#define ADM8211_IER_TSCZE (1 << 25)
+#define ADM8211_IER_LinkOnIE (1 << 24)
+#define ADM8211_IER_SQLIE (1 << 23)
+#define ADM8211_IER_WEPIE (1 << 22)
+#define ADM8211_IER_ATIMEIE (1 << 21)
+#define ADM8211_IER_TBTTIE (1 << 20)
+#define ADM8211_IER_NIE (1 << 16)
+#define ADM8211_IER_AIE (1 << 15)
+#define ADM8211_IER_TEIE (1 << 14)
+#define ADM8211_IER_FBEIE (1 << 13)
+#define ADM8211_IER_REIE (1 << 12)
+#define ADM8211_IER_GPTIE (1 << 11)
+#define ADM8211_IER_RSIE (1 << 8)
+#define ADM8211_IER_RUIE (1 << 7)
+#define ADM8211_IER_RCIE (1 << 6)
+#define ADM8211_IER_TUIE (1 << 5)
+#define ADM8211_IER_TRTIE (1 << 4)
+#define ADM8211_IER_TLTTIE (1 << 3)
+#define ADM8211_IER_TDUIE (1 << 2)
+#define ADM8211_IER_TPSIE (1 << 1)
+#define ADM8211_IER_TCIE (1 << 0)
+
+/* CSR9 - SPR (Serial Port Register) */
+#define ADM8211_SPR_SRS (1 << 11)
+#define ADM8211_SPR_SDO (1 << 3)
+#define ADM8211_SPR_SDI (1 << 2)
+#define ADM8211_SPR_SCLK (1 << 1)
+#define ADM8211_SPR_SCS (1 << 0)
+
+/* CSR9A - CSR_TEST0 */
+#define ADM8211_CSR_TEST0_EPNE (1 << 18)
+#define ADM8211_CSR_TEST0_EPSNM (1 << 17)
+#define ADM8211_CSR_TEST0_EPTYP (1 << 16)
+#define ADM8211_CSR_TEST0_EPRLD (1 << 15)
+
+/* CSR10 - WCSR (Wake-up Control/Status Register) */
+#define ADM8211_WCSR_CRCT (1 << 30)
+#define ADM8211_WCSR_TSFTWE (1 << 20)
+#define ADM8211_WCSR_TIMWE (1 << 19)
+#define ADM8211_WCSR_ATIMWE (1 << 18)
+#define ADM8211_WCSR_KEYWE (1 << 17)
+#define ADM8211_WCSR_MPRE (1 << 9)
+#define ADM8211_WCSR_LSOE (1 << 8)
+#define ADM8211_WCSR_KEYUP (1 << 6)
+#define ADM8211_WCSR_TSFTW (1 << 5)
+#define ADM8211_WCSR_TIMW (1 << 4)
+#define ADM8211_WCSR_ATIMW (1 << 3)
+#define ADM8211_WCSR_MPR (1 << 1)
+#define ADM8211_WCSR_LSO (1 << 0)
+
+/* CSR11A - GPIO */
+#define ADM8211_CSR_GPIO_EN5 (1 << 17)
+#define ADM8211_CSR_GPIO_EN4 (1 << 16)
+#define ADM8211_CSR_GPIO_EN3 (1 << 15)
+#define ADM8211_CSR_GPIO_EN2 (1 << 14)
+#define ADM8211_CSR_GPIO_EN1 (1 << 13)
+#define ADM8211_CSR_GPIO_EN0 (1 << 12)
+#define ADM8211_CSR_GPIO_O5 (1 << 11)
+#define ADM8211_CSR_GPIO_O4 (1 << 10)
+#define ADM8211_CSR_GPIO_O3 (1 << 9)
+#define ADM8211_CSR_GPIO_O2 (1 << 8)
+#define ADM8211_CSR_GPIO_O1 (1 << 7)
+#define ADM8211_CSR_GPIO_O0 (1 << 6)
+#define ADM8211_CSR_GPIO_IN 0x0000003f
+
+/* CSR12 - BBPCTL (BBP Control port) */
+#define ADM8211_BBPCTL_MMISEL (1 << 31)
+#define ADM8211_BBPCTL_SPICADD (0x7F << 24)
+#define ADM8211_BBPCTL_RF3000 (0x20 << 24)
+#define ADM8211_BBPCTL_TXCE (1 << 23)
+#define ADM8211_BBPCTL_RXCE (1 << 22)
+#define ADM8211_BBPCTL_CCAP (1 << 21)
+#define ADM8211_BBPCTL_TYPE 0x001c0000
+#define ADM8211_BBPCTL_WR (1 << 17)
+#define ADM8211_BBPCTL_RD (1 << 16)
+#define ADM8211_BBPCTL_ADDR 0x0000ff00
+#define ADM8211_BBPCTL_DATA 0x000000ff
+
+/* CSR12A - SYNCTL (Synthesizer Control port) */
+#define ADM8211_SYNCTL_WR (1 << 31)
+#define ADM8211_SYNCTL_RD (1 << 30)
+#define ADM8211_SYNCTL_CS0 (1 << 29)
+#define ADM8211_SYNCTL_CS1 (1 << 28)
+#define ADM8211_SYNCTL_CAL (1 << 27)
+#define ADM8211_SYNCTL_SELCAL (1 << 26)
+#define ADM8211_SYNCTL_RFtype ((1 << 24) | (1 << 23) | (1 << 22))
+#define ADM8211_SYNCTL_RFMD (1 << 22)
+#define ADM8211_SYNCTL_GENERAL (0x7 << 22)
+/* SYNCTL 21:0 Data (Si4126: 18-bit data, 4-bit address) */
+
+/* CSR18 - CMDR (Command Register) */
+#define ADM8211_CMDR_PM (1 << 19)
+#define ADM8211_CMDR_APM (1 << 18)
+#define ADM8211_CMDR_RTE (1 << 4)
+#define ADM8211_CMDR_DRT ((1 << 3) | (1 << 2))
+#define ADM8211_CMDR_DRT_8DW (0x0 << 2)
+#define ADM8211_CMDR_DRT_16DW (0x1 << 2)
+#define ADM8211_CMDR_DRT_SF (0x2 << 2)
+
+/* CSR33 - SYNRF (SYNRF direct control) */
+#define ADM8211_SYNRF_SELSYN (1 << 31)
+#define ADM8211_SYNRF_SELRF (1 << 30)
+#define ADM8211_SYNRF_LERF (1 << 29)
+#define ADM8211_SYNRF_LEIF (1 << 28)
+#define ADM8211_SYNRF_SYNCLK (1 << 27)
+#define ADM8211_SYNRF_SYNDATA (1 << 26)
+#define ADM8211_SYNRF_PE1 (1 << 25)
+#define ADM8211_SYNRF_PE2 (1 << 24)
+#define ADM8211_SYNRF_PA_PE (1 << 23)
+#define ADM8211_SYNRF_TR_SW (1 << 22)
+#define ADM8211_SYNRF_TR_SWN (1 << 21)
+#define ADM8211_SYNRF_RADIO (1 << 20)
+#define ADM8211_SYNRF_CAL_EN (1 << 19)
+#define ADM8211_SYNRF_PHYRST (1 << 18)
+
+#define ADM8211_SYNRF_IF_SELECT_0 (1 << 31)
+#define ADM8211_SYNRF_IF_SELECT_1 ((1 << 31) | (1 << 28))
+#define ADM8211_SYNRF_WRITE_SYNDATA_0 (1 << 31)
+#define ADM8211_SYNRF_WRITE_SYNDATA_1 ((1 << 31) | (1 << 26))
+#define ADM8211_SYNRF_WRITE_CLOCK_0 (1 << 31)
+#define ADM8211_SYNRF_WRITE_CLOCK_1 ((1 << 31) | (1 << 27))
+
+/* CSR44 - WEPCTL (WEP Control) */
+#define ADM8211_WEPCTL_WEPENABLE (1 << 31)
+#define ADM8211_WEPCTL_WPAENABLE (1 << 30)
+#define ADM8211_WEPCTL_CURRENT_TABLE (1 << 29)
+#define ADM8211_WEPCTL_TABLE_WR (1 << 28)
+#define ADM8211_WEPCTL_TABLE_RD (1 << 27)
+#define ADM8211_WEPCTL_WEPRXBYP (1 << 25)
+#define ADM8211_WEPCTL_SEL_WEPTABLE (1 << 23)
+#define ADM8211_WEPCTL_ADDR (0x000001ff)
+
+/* CSR45 - WESK (Data Entry for Share/Individual Key) */
+#define ADM8211_WESK_DATA (0x0000ffff)
+
+/* FER (Function Event Register) */
+#define ADM8211_FER_INTR_EV_ENT (1 << 15)
+
+
+/* Si4126 RF Synthesizer - Control Registers */
+#define SI4126_MAIN_CONF 0
+#define SI4126_PHASE_DET_GAIN 1
+#define SI4126_POWERDOWN 2
+#define SI4126_RF1_N_DIV 3 /* only Si4136 */
+#define SI4126_RF2_N_DIV 4
+#define SI4126_IF_N_DIV 5
+#define SI4126_RF1_R_DIV 6 /* only Si4136 */
+#define SI4126_RF2_R_DIV 7
+#define SI4126_IF_R_DIV 8
+
+/* Main Configuration */
+#define SI4126_MAIN_XINDIV2 (1 << 6)
+#define SI4126_MAIN_IFDIV ((1 << 11) | (1 << 10))
+/* Powerdown */
+#define SI4126_POWERDOWN_PDIB (1 << 1)
+#define SI4126_POWERDOWN_PDRB (1 << 0)
+
+
+/* RF3000 BBP - Control Port Registers */
+/* 0x00 - reserved */
+#define RF3000_MODEM_CTRL__RX_STATUS 0x01
+#define RF3000_CCA_CTRL 0x02
+#define RF3000_DIVERSITY__RSSI 0x03
+#define RF3000_RX_SIGNAL_FIELD 0x04
+#define RF3000_RX_LEN_MSB 0x05
+#define RF3000_RX_LEN_LSB 0x06
+#define RF3000_RX_SERVICE_FIELD 0x07
+#define RF3000_TX_VAR_GAIN__TX_LEN_EXT 0x11
+#define RF3000_TX_LEN_MSB 0x12
+#define RF3000_TX_LEN_LSB 0x13
+#define RF3000_LOW_GAIN_CALIB 0x14
+#define RF3000_HIGH_GAIN_CALIB 0x15
+
+/* ADM8211 revisions */
+#define ADM8211_REV_AB 0x11
+#define ADM8211_REV_AF 0x15
+#define ADM8211_REV_BA 0x20
+#define ADM8211_REV_CA 0x30
+
+struct adm8211_desc {
+ __le32 status;
+ __le32 length;
+ __le32 buffer1;
+ __le32 buffer2;
+};
+
+#define RDES0_STATUS_OWN (1 << 31)
+#define RDES0_STATUS_ES (1 << 30)
+#define RDES0_STATUS_SQL (1 << 29)
+#define RDES0_STATUS_DE (1 << 28)
+#define RDES0_STATUS_FS (1 << 27)
+#define RDES0_STATUS_LS (1 << 26)
+#define RDES0_STATUS_PCF (1 << 25)
+#define RDES0_STATUS_SFDE (1 << 24)
+#define RDES0_STATUS_SIGE (1 << 23)
+#define RDES0_STATUS_CRC16E (1 << 22)
+#define RDES0_STATUS_RXTOE (1 << 21)
+#define RDES0_STATUS_CRC32E (1 << 20)
+#define RDES0_STATUS_ICVE (1 << 19)
+#define RDES0_STATUS_DA1 (1 << 17)
+#define RDES0_STATUS_DA0 (1 << 16)
+#define RDES0_STATUS_RXDR ((1 << 15) | (1 << 14) | (1 << 13) | (1 << 12))
+#define RDES0_STATUS_FL (0x00000fff)
+
+#define RDES1_CONTROL_RER (1 << 25)
+#define RDES1_CONTROL_RCH (1 << 24)
+#define RDES1_CONTROL_RBS2 (0x00fff000)
+#define RDES1_CONTROL_RBS1 (0x00000fff)
+
+#define RDES1_STATUS_RSSI (0x0000007f)
+
+
+#define TDES0_CONTROL_OWN (1 << 31)
+#define TDES0_CONTROL_DONE (1 << 30)
+#define TDES0_CONTROL_TXDR (0x0ff00000)
+
+#define TDES0_STATUS_OWN (1 << 31)
+#define TDES0_STATUS_DONE (1 << 30)
+#define TDES0_STATUS_ES (1 << 29)
+#define TDES0_STATUS_TLT (1 << 28)
+#define TDES0_STATUS_TRT (1 << 27)
+#define TDES0_STATUS_TUF (1 << 26)
+#define TDES0_STATUS_TRO (1 << 25)
+#define TDES0_STATUS_SOFBR (1 << 24)
+#define TDES0_STATUS_ACR (0x00000fff)
+
+#define TDES1_CONTROL_IC (1 << 31)
+#define TDES1_CONTROL_LS (1 << 30)
+#define TDES1_CONTROL_FS (1 << 29)
+#define TDES1_CONTROL_TER (1 << 25)
+#define TDES1_CONTROL_TCH (1 << 24)
+#define TDES1_CONTROL_RBS2 (0x00fff000)
+#define TDES1_CONTROL_RBS1 (0x00000fff)
+
+/* SRAM offsets */
+#define ADM8211_SRAM(x) (priv->pdev->revision < ADM8211_REV_BA ? \
+ ADM8211_SRAM_A_ ## x : ADM8211_SRAM_B_ ## x)
+
+#define ADM8211_SRAM_INDIV_KEY 0x0000
+#define ADM8211_SRAM_A_SHARE_KEY 0x0160
+#define ADM8211_SRAM_B_SHARE_KEY 0x00c0
+
+#define ADM8211_SRAM_A_SSID 0x0180
+#define ADM8211_SRAM_B_SSID 0x00d4
+#define ADM8211_SRAM_SSID ADM8211_SRAM(SSID)
+
+#define ADM8211_SRAM_A_SUPP_RATE 0x0191
+#define ADM8211_SRAM_B_SUPP_RATE 0x00dd
+#define ADM8211_SRAM_SUPP_RATE ADM8211_SRAM(SUPP_RATE)
+
+#define ADM8211_SRAM_A_SIZE 0x0200
+#define ADM8211_SRAM_B_SIZE 0x01c0
+#define ADM8211_SRAM_SIZE ADM8211_SRAM(SIZE)
+
+struct adm8211_rx_ring_info {
+ struct sk_buff *skb;
+ dma_addr_t mapping;
+};
+
+struct adm8211_tx_ring_info {
+ struct sk_buff *skb;
+ dma_addr_t mapping;
+ size_t hdrlen;
+};
+
+#define PLCP_SIGNAL_1M 0x0a
+#define PLCP_SIGNAL_2M 0x14
+#define PLCP_SIGNAL_5M5 0x37
+#define PLCP_SIGNAL_11M 0x6e
+
+struct adm8211_tx_hdr {
+ u8 da[6];
+ u8 signal; /* PLCP signal / TX rate in 100 Kbps */
+ u8 service;
+ __le16 frame_body_size;
+ __le16 frame_control;
+ __le16 plcp_frag_tail_len;
+ __le16 plcp_frag_head_len;
+ __le16 dur_frag_tail;
+ __le16 dur_frag_head;
+ u8 addr4[6];
+
+#define ADM8211_TXHDRCTL_SHORT_PREAMBLE (1 << 0)
+#define ADM8211_TXHDRCTL_MORE_FRAG (1 << 1)
+#define ADM8211_TXHDRCTL_MORE_DATA (1 << 2)
+#define ADM8211_TXHDRCTL_FRAG_NO (1 << 3) /* ? */
+#define ADM8211_TXHDRCTL_ENABLE_RTS (1 << 4)
+#define ADM8211_TXHDRCTL_ENABLE_WEP_ENGINE (1 << 5)
+#define ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER (1 << 15) /* ? */
+ __le16 header_control;
+ __le16 frag;
+ u8 reserved_0;
+ u8 retry_limit;
+
+ u32 wep2key0;
+ u32 wep2key1;
+ u32 wep2key2;
+ u32 wep2key3;
+
+ u8 keyid;
+ u8 entry_control; // huh??
+ u16 reserved_1;
+ u32 reserved_2;
+} __packed;
+
+
+#define RX_COPY_BREAK 128
+#define RX_PKT_SIZE 2500
+
+struct adm8211_eeprom {
+ __le16 signature; /* 0x00 */
+ u8 major_version; /* 0x02 */
+ u8 minor_version; /* 0x03 */
+ u8 reserved_1[4]; /* 0x04 */
+ u8 hwaddr[6]; /* 0x08 */
+ u8 reserved_2[8]; /* 0x1E */
+ __le16 cr49; /* 0x16 */
+ u8 cr03; /* 0x18 */
+ u8 cr28; /* 0x19 */
+ u8 cr29; /* 0x1A */
+ u8 country_code; /* 0x1B */
+
+/* specific bbp types */
+#define ADM8211_BBP_RFMD3000 0x00
+#define ADM8211_BBP_RFMD3002 0x01
+#define ADM8211_BBP_ADM8011 0x04
+ u8 specific_bbptype; /* 0x1C */
+ u8 specific_rftype; /* 0x1D */
+ u8 reserved_3[2]; /* 0x1E */
+ __le16 device_id; /* 0x20 */
+ __le16 vendor_id; /* 0x22 */
+ __le16 subsystem_id; /* 0x24 */
+ __le16 subsystem_vendor_id; /* 0x26 */
+ u8 maxlat; /* 0x28 */
+ u8 mingnt; /* 0x29 */
+ __le16 cis_pointer_low; /* 0x2A */
+ __le16 cis_pointer_high; /* 0x2C */
+ __le16 csr18; /* 0x2E */
+ u8 reserved_4[16]; /* 0x30 */
+ u8 d1_pwrdara; /* 0x40 */
+ u8 d0_pwrdara; /* 0x41 */
+ u8 d3_pwrdara; /* 0x42 */
+ u8 d2_pwrdara; /* 0x43 */
+ u8 antenna_power[14]; /* 0x44 */
+ __le16 cis_wordcnt; /* 0x52 */
+ u8 tx_power[14]; /* 0x54 */
+ u8 lpf_cutoff[14]; /* 0x62 */
+ u8 lnags_threshold[14]; /* 0x70 */
+ __le16 checksum; /* 0x7E */
+ u8 cis_data[0]; /* 0x80, 384 bytes */
+} __packed;
+
+struct adm8211_priv {
+ struct pci_dev *pdev;
+ spinlock_t lock;
+ struct adm8211_csr __iomem *map;
+ struct adm8211_desc *rx_ring;
+ struct adm8211_desc *tx_ring;
+ dma_addr_t rx_ring_dma;
+ dma_addr_t tx_ring_dma;
+ struct adm8211_rx_ring_info *rx_buffers;
+ struct adm8211_tx_ring_info *tx_buffers;
+ unsigned int rx_ring_size, tx_ring_size;
+ unsigned int cur_tx, dirty_tx, cur_rx;
+
+ struct ieee80211_low_level_stats stats;
+ struct ieee80211_supported_band band;
+ struct ieee80211_channel channels[14];
+ int mode;
+
+ int channel;
+ u8 bssid[ETH_ALEN];
+
+ u8 soft_rx_crc;
+ u8 retry_limit;
+
+ u8 ant_power;
+ u8 tx_power;
+ u8 lpf_cutoff;
+ u8 lnags_threshold;
+ struct adm8211_eeprom *eeprom;
+ size_t eeprom_len;
+
+ u32 nar;
+
+#define ADM8211_TYPE_INTERSIL 0x00
+#define ADM8211_TYPE_RFMD 0x01
+#define ADM8211_TYPE_MARVEL 0x02
+#define ADM8211_TYPE_AIROHA 0x03
+#define ADM8211_TYPE_ADMTEK 0x05
+ unsigned int rf_type:3;
+ unsigned int bbp_type:3;
+
+ u8 specific_bbptype;
+ enum {
+ ADM8211_RFMD2948 = 0x0,
+ ADM8211_RFMD2958 = 0x1,
+ ADM8211_RFMD2958_RF3000_CONTROL_POWER = 0x2,
+ ADM8211_MAX2820 = 0x8,
+ ADM8211_AL2210L = 0xC, /* Airoha */
+ } transceiver_type;
+};
+
+struct ieee80211_chan_range {
+ u8 min;
+ u8 max;
+};
+
+static const struct ieee80211_chan_range cranges[] = {
+ {1, 11}, /* FCC */
+ {1, 11}, /* IC */
+ {1, 13}, /* ETSI */
+ {10, 11}, /* SPAIN */
+ {10, 13}, /* FRANCE */
+ {14, 14}, /* MMK */
+ {1, 14}, /* MMK2 */
+};
+
+#endif /* ADM8211_H */
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c
new file mode 100644
index 0000000..4045e5a
--- /dev/null
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -0,0 +1,2538 @@
+/*
+ * at76c503/at76c505 USB driver
+ *
+ * Copyright (c) 2002 - 2003 Oliver Kurth
+ * Copyright (c) 2004 Joerg Albert <joerg.albert@gmx.de>
+ * Copyright (c) 2004 Nick Jones
+ * Copyright (c) 2004 Balint Seeber <n0_5p4m_p13453@hotmail.com>
+ * Copyright (c) 2007 Guido Guenther <agx@sigxcpu.org>
+ * Copyright (c) 2007 Kalle Valo <kalle.valo@iki.fi>
+ * Copyright (c) 2010 Sebastian Smolorz <sesmo@gmx.net>
+ *
+ * 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 file is part of the Berlios driver for WLAN USB devices based on the
+ * Atmel AT76C503A/505/505A.
+ *
+ * Some iw_handler code was taken from airo.c, (C) 1999 Benjamin Reed
+ *
+ * TODO list is at the wiki:
+ *
+ * http://wireless.kernel.org/en/users/Drivers/at76c50x-usb#TODO
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#include <net/ieee80211_radiotap.h>
+#include <linux/firmware.h>
+#include <linux/leds.h>
+#include <net/mac80211.h>
+
+#include "at76c50x-usb.h"
+
+/* Version information */
+#define DRIVER_NAME "at76c50x-usb"
+#define DRIVER_VERSION "0.17"
+#define DRIVER_DESC "Atmel at76x USB Wireless LAN Driver"
+
+/* at76_debug bits */
+#define DBG_PROGRESS 0x00000001 /* authentication/accociation */
+#define DBG_BSS_TABLE 0x00000002 /* show BSS table after scans */
+#define DBG_IOCTL 0x00000004 /* ioctl calls / settings */
+#define DBG_MAC_STATE 0x00000008 /* MAC state transitions */
+#define DBG_TX_DATA 0x00000010 /* tx header */
+#define DBG_TX_DATA_CONTENT 0x00000020 /* tx content */
+#define DBG_TX_MGMT 0x00000040 /* tx management */
+#define DBG_RX_DATA 0x00000080 /* rx data header */
+#define DBG_RX_DATA_CONTENT 0x00000100 /* rx data content */
+#define DBG_RX_MGMT 0x00000200 /* rx mgmt frame headers */
+#define DBG_RX_BEACON 0x00000400 /* rx beacon */
+#define DBG_RX_CTRL 0x00000800 /* rx control */
+#define DBG_RX_MGMT_CONTENT 0x00001000 /* rx mgmt content */
+#define DBG_RX_FRAGS 0x00002000 /* rx data fragment handling */
+#define DBG_DEVSTART 0x00004000 /* fw download, device start */
+#define DBG_URB 0x00008000 /* rx urb status, ... */
+#define DBG_RX_ATMEL_HDR 0x00010000 /* Atmel-specific Rx headers */
+#define DBG_PROC_ENTRY 0x00020000 /* procedure entries/exits */
+#define DBG_PM 0x00040000 /* power management settings */
+#define DBG_BSS_MATCH 0x00080000 /* BSS match failures */
+#define DBG_PARAMS 0x00100000 /* show configured parameters */
+#define DBG_WAIT_COMPLETE 0x00200000 /* command completion */
+#define DBG_RX_FRAGS_SKB 0x00400000 /* skb header of Rx fragments */
+#define DBG_BSS_TABLE_RM 0x00800000 /* purging bss table entries */
+#define DBG_MONITOR_MODE 0x01000000 /* monitor mode */
+#define DBG_MIB 0x02000000 /* dump all MIBs on startup */
+#define DBG_MGMT_TIMER 0x04000000 /* dump mgmt_timer ops */
+#define DBG_WE_EVENTS 0x08000000 /* dump wireless events */
+#define DBG_FW 0x10000000 /* firmware download */
+#define DBG_DFU 0x20000000 /* device firmware upgrade */
+#define DBG_CMD 0x40000000
+#define DBG_MAC80211 0x80000000
+
+#define DBG_DEFAULTS 0
+
+/* Use our own dbg macro */
+#define at76_dbg(bits, format, arg...) \
+do { \
+ if (at76_debug & (bits)) \
+ printk(KERN_DEBUG DRIVER_NAME ": " format "\n", ##arg); \
+} while (0)
+
+#define at76_dbg_dump(bits, buf, len, format, arg...) \
+do { \
+ if (at76_debug & (bits)) { \
+ printk(KERN_DEBUG DRIVER_NAME ": " format "\n", ##arg); \
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len); \
+ } \
+} while (0)
+
+static uint at76_debug = DBG_DEFAULTS;
+
+/* Protect against concurrent firmware loading and parsing */
+static struct mutex fw_mutex;
+
+static struct fwentry firmwares[] = {
+ [0] = { "" },
+ [BOARD_503_ISL3861] = { "atmel_at76c503-i3861.bin" },
+ [BOARD_503_ISL3863] = { "atmel_at76c503-i3863.bin" },
+ [BOARD_503] = { "atmel_at76c503-rfmd.bin" },
+ [BOARD_503_ACC] = { "atmel_at76c503-rfmd-acc.bin" },
+ [BOARD_505] = { "atmel_at76c505-rfmd.bin" },
+ [BOARD_505_2958] = { "atmel_at76c505-rfmd2958.bin" },
+ [BOARD_505A] = { "atmel_at76c505a-rfmd2958.bin" },
+ [BOARD_505AMX] = { "atmel_at76c505amx-rfmd.bin" },
+};
+MODULE_FIRMWARE("atmel_at76c503-i3861.bin");
+MODULE_FIRMWARE("atmel_at76c503-i3863.bin");
+MODULE_FIRMWARE("atmel_at76c503-rfmd.bin");
+MODULE_FIRMWARE("atmel_at76c503-rfmd-acc.bin");
+MODULE_FIRMWARE("atmel_at76c505-rfmd.bin");
+MODULE_FIRMWARE("atmel_at76c505-rfmd2958.bin");
+MODULE_FIRMWARE("atmel_at76c505a-rfmd2958.bin");
+MODULE_FIRMWARE("atmel_at76c505amx-rfmd.bin");
+
+#define USB_DEVICE_DATA(__ops) .driver_info = (kernel_ulong_t)(__ops)
+
+static struct usb_device_id dev_table[] = {
+ /*
+ * at76c503-i3861
+ */
+ /* Generic AT76C503/3861 device */
+ { USB_DEVICE(0x03eb, 0x7603), USB_DEVICE_DATA(BOARD_503_ISL3861) },
+ /* Linksys WUSB11 v2.1/v2.6 */
+ { USB_DEVICE(0x066b, 0x2211), USB_DEVICE_DATA(BOARD_503_ISL3861) },
+ /* Netgear MA101 rev. A */
+ { USB_DEVICE(0x0864, 0x4100), USB_DEVICE_DATA(BOARD_503_ISL3861) },
+ /* Tekram U300C / Allnet ALL0193 */
+ { USB_DEVICE(0x0b3b, 0x1612), USB_DEVICE_DATA(BOARD_503_ISL3861) },
+ /* HP HN210W J7801A */
+ { USB_DEVICE(0x03f0, 0x011c), USB_DEVICE_DATA(BOARD_503_ISL3861) },
+ /* Sitecom/Z-Com/Zyxel M4Y-750 */
+ { USB_DEVICE(0x0cde, 0x0001), USB_DEVICE_DATA(BOARD_503_ISL3861) },
+ /* Dynalink/Askey WLL013 (intersil) */
+ { USB_DEVICE(0x069a, 0x0320), USB_DEVICE_DATA(BOARD_503_ISL3861) },
+ /* EZ connect 11Mpbs Wireless USB Adapter SMC2662W v1 */
+ { USB_DEVICE(0x0d5c, 0xa001), USB_DEVICE_DATA(BOARD_503_ISL3861) },
+ /* BenQ AWL300 */
+ { USB_DEVICE(0x04a5, 0x9000), USB_DEVICE_DATA(BOARD_503_ISL3861) },
+ /* Addtron AWU-120, Compex WLU11 */
+ { USB_DEVICE(0x05dd, 0xff31), USB_DEVICE_DATA(BOARD_503_ISL3861) },
+ /* Intel AP310 AnyPoint II USB */
+ { USB_DEVICE(0x8086, 0x0200), USB_DEVICE_DATA(BOARD_503_ISL3861) },
+ /* Dynalink L11U */
+ { USB_DEVICE(0x0d8e, 0x7100), USB_DEVICE_DATA(BOARD_503_ISL3861) },
+ /* Arescom WL-210, FCC id 07J-GL2411USB */
+ { USB_DEVICE(0x0d8e, 0x7110), USB_DEVICE_DATA(BOARD_503_ISL3861) },
+ /* I-O DATA WN-B11/USB */
+ { USB_DEVICE(0x04bb, 0x0919), USB_DEVICE_DATA(BOARD_503_ISL3861) },
+ /* BT Voyager 1010 */
+ { USB_DEVICE(0x069a, 0x0821), USB_DEVICE_DATA(BOARD_503_ISL3861) },
+ /*
+ * at76c503-i3863
+ */
+ /* Generic AT76C503/3863 device */
+ { USB_DEVICE(0x03eb, 0x7604), USB_DEVICE_DATA(BOARD_503_ISL3863) },
+ /* Samsung SWL-2100U */
+ { USB_DEVICE(0x055d, 0xa000), USB_DEVICE_DATA(BOARD_503_ISL3863) },
+ /*
+ * at76c503-rfmd
+ */
+ /* Generic AT76C503/RFMD device */
+ { USB_DEVICE(0x03eb, 0x7605), USB_DEVICE_DATA(BOARD_503) },
+ /* Dynalink/Askey WLL013 (rfmd) */
+ { USB_DEVICE(0x069a, 0x0321), USB_DEVICE_DATA(BOARD_503) },
+ /* Linksys WUSB11 v2.6 */
+ { USB_DEVICE(0x077b, 0x2219), USB_DEVICE_DATA(BOARD_503) },
+ /* Network Everywhere NWU11B */
+ { USB_DEVICE(0x077b, 0x2227), USB_DEVICE_DATA(BOARD_503) },
+ /* Netgear MA101 rev. B */
+ { USB_DEVICE(0x0864, 0x4102), USB_DEVICE_DATA(BOARD_503) },
+ /* D-Link DWL-120 rev. E */
+ { USB_DEVICE(0x2001, 0x3200), USB_DEVICE_DATA(BOARD_503) },
+ /* Actiontec 802UAT1, HWU01150-01UK */
+ { USB_DEVICE(0x1668, 0x7605), USB_DEVICE_DATA(BOARD_503) },
+ /* AirVast W-Buddie WN210 */
+ { USB_DEVICE(0x03eb, 0x4102), USB_DEVICE_DATA(BOARD_503) },
+ /* Dick Smith Electronics XH1153 802.11b USB adapter */
+ { USB_DEVICE(0x1371, 0x5743), USB_DEVICE_DATA(BOARD_503) },
+ /* CNet CNUSB611 */
+ { USB_DEVICE(0x1371, 0x0001), USB_DEVICE_DATA(BOARD_503) },
+ /* FiberLine FL-WL200U */
+ { USB_DEVICE(0x1371, 0x0002), USB_DEVICE_DATA(BOARD_503) },
+ /* BenQ AWL400 USB stick */
+ { USB_DEVICE(0x04a5, 0x9001), USB_DEVICE_DATA(BOARD_503) },
+ /* 3Com 3CRSHEW696 */
+ { USB_DEVICE(0x0506, 0x0a01), USB_DEVICE_DATA(BOARD_503) },
+ /* Siemens Santis ADSL WLAN USB adapter WLL 013 */
+ { USB_DEVICE(0x0681, 0x001b), USB_DEVICE_DATA(BOARD_503) },
+ /* Belkin F5D6050, version 2 */
+ { USB_DEVICE(0x050d, 0x0050), USB_DEVICE_DATA(BOARD_503) },
+ /* iBlitzz, BWU613 (not *B or *SB) */
+ { USB_DEVICE(0x07b8, 0xb000), USB_DEVICE_DATA(BOARD_503) },
+ /* Gigabyte GN-WLBM101 */
+ { USB_DEVICE(0x1044, 0x8003), USB_DEVICE_DATA(BOARD_503) },
+ /* Planex GW-US11S */
+ { USB_DEVICE(0x2019, 0x3220), USB_DEVICE_DATA(BOARD_503) },
+ /* Internal WLAN adapter in h5[4,5]xx series iPAQs */
+ { USB_DEVICE(0x049f, 0x0032), USB_DEVICE_DATA(BOARD_503) },
+ /* Corega Wireless LAN USB-11 mini */
+ { USB_DEVICE(0x07aa, 0x0011), USB_DEVICE_DATA(BOARD_503) },
+ /* Corega Wireless LAN USB-11 mini2 */
+ { USB_DEVICE(0x07aa, 0x0018), USB_DEVICE_DATA(BOARD_503) },
+ /* Uniden PCW100 */
+ { USB_DEVICE(0x05dd, 0xff35), USB_DEVICE_DATA(BOARD_503) },
+ /*
+ * at76c503-rfmd-acc
+ */
+ /* SMC2664W */
+ { USB_DEVICE(0x083a, 0x3501), USB_DEVICE_DATA(BOARD_503_ACC) },
+ /* Belkin F5D6050, SMC2662W v2, SMC2662W-AR */
+ { USB_DEVICE(0x0d5c, 0xa002), USB_DEVICE_DATA(BOARD_503_ACC) },
+ /*
+ * at76c505-rfmd
+ */
+ /* Generic AT76C505/RFMD */
+ { USB_DEVICE(0x03eb, 0x7606), USB_DEVICE_DATA(BOARD_505) },
+ /*
+ * at76c505-rfmd2958
+ */
+ /* Generic AT76C505/RFMD, OvisLink WL-1130USB */
+ { USB_DEVICE(0x03eb, 0x7613), USB_DEVICE_DATA(BOARD_505_2958) },
+ /* Fiberline FL-WL240U */
+ { USB_DEVICE(0x1371, 0x0014), USB_DEVICE_DATA(BOARD_505_2958) },
+ /* CNet CNUSB-611G */
+ { USB_DEVICE(0x1371, 0x0013), USB_DEVICE_DATA(BOARD_505_2958) },
+ /* Linksys WUSB11 v2.8 */
+ { USB_DEVICE(0x1915, 0x2233), USB_DEVICE_DATA(BOARD_505_2958) },
+ /* Xterasys XN-2122B, IBlitzz BWU613B/BWU613SB */
+ { USB_DEVICE(0x12fd, 0x1001), USB_DEVICE_DATA(BOARD_505_2958) },
+ /* Corega WLAN USB Stick 11 */
+ { USB_DEVICE(0x07aa, 0x7613), USB_DEVICE_DATA(BOARD_505_2958) },
+ /* Microstar MSI Box MS6978 */
+ { USB_DEVICE(0x0db0, 0x1020), USB_DEVICE_DATA(BOARD_505_2958) },
+ /*
+ * at76c505a-rfmd2958
+ */
+ /* Generic AT76C505A device */
+ { USB_DEVICE(0x03eb, 0x7614), USB_DEVICE_DATA(BOARD_505A) },
+ /* Generic AT76C505AS device */
+ { USB_DEVICE(0x03eb, 0x7617), USB_DEVICE_DATA(BOARD_505A) },
+ /* Siemens Gigaset USB WLAN Adapter 11 */
+ { USB_DEVICE(0x1690, 0x0701), USB_DEVICE_DATA(BOARD_505A) },
+ /* OQO Model 01+ Internal Wi-Fi */
+ { USB_DEVICE(0x1557, 0x0002), USB_DEVICE_DATA(BOARD_505A) },
+ /*
+ * at76c505amx-rfmd
+ */
+ /* Generic AT76C505AMX device */
+ { USB_DEVICE(0x03eb, 0x7615), USB_DEVICE_DATA(BOARD_505AMX) },
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, dev_table);
+
+/* Supported rates of this hardware, bit 7 marks basic rates */
+static const u8 hw_rates[] = { 0x82, 0x84, 0x0b, 0x16 };
+
+static const char *const preambles[] = { "long", "short", "auto" };
+
+/* Firmware download */
+/* DFU states */
+#define STATE_IDLE 0x00
+#define STATE_DETACH 0x01
+#define STATE_DFU_IDLE 0x02
+#define STATE_DFU_DOWNLOAD_SYNC 0x03
+#define STATE_DFU_DOWNLOAD_BUSY 0x04
+#define STATE_DFU_DOWNLOAD_IDLE 0x05
+#define STATE_DFU_MANIFEST_SYNC 0x06
+#define STATE_DFU_MANIFEST 0x07
+#define STATE_DFU_MANIFEST_WAIT_RESET 0x08
+#define STATE_DFU_UPLOAD_IDLE 0x09
+#define STATE_DFU_ERROR 0x0a
+
+/* DFU commands */
+#define DFU_DETACH 0
+#define DFU_DNLOAD 1
+#define DFU_UPLOAD 2
+#define DFU_GETSTATUS 3
+#define DFU_CLRSTATUS 4
+#define DFU_GETSTATE 5
+#define DFU_ABORT 6
+
+#define FW_BLOCK_SIZE 1024
+
+struct dfu_status {
+ unsigned char status;
+ unsigned char poll_timeout[3];
+ unsigned char state;
+ unsigned char string;
+} __packed;
+
+static inline int at76_is_intersil(enum board_type board)
+{
+ return (board == BOARD_503_ISL3861 || board == BOARD_503_ISL3863);
+}
+
+static inline int at76_is_503rfmd(enum board_type board)
+{
+ return (board == BOARD_503 || board == BOARD_503_ACC);
+}
+
+static inline int at76_is_505a(enum board_type board)
+{
+ return (board == BOARD_505A || board == BOARD_505AMX);
+}
+
+/* Load a block of the first (internal) part of the firmware */
+static int at76_load_int_fw_block(struct usb_device *udev, int blockno,
+ void *block, int size)
+{
+ return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), DFU_DNLOAD,
+ USB_TYPE_CLASS | USB_DIR_OUT |
+ USB_RECIP_INTERFACE, blockno, 0, block, size,
+ USB_CTRL_GET_TIMEOUT);
+}
+
+static int at76_dfu_get_status(struct usb_device *udev,
+ struct dfu_status *status)
+{
+ int ret;
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATUS,
+ USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
+ 0, 0, status, sizeof(struct dfu_status),
+ USB_CTRL_GET_TIMEOUT);
+ return ret;
+}
+
+static u8 at76_dfu_get_state(struct usb_device *udev, u8 *state)
+{
+ int ret;
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATE,
+ USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
+ 0, 0, state, 1, USB_CTRL_GET_TIMEOUT);
+ return ret;
+}
+
+/* Convert timeout from the DFU status to jiffies */
+static inline unsigned long at76_get_timeout(struct dfu_status *s)
+{
+ return msecs_to_jiffies((s->poll_timeout[2] << 16)
+ | (s->poll_timeout[1] << 8)
+ | (s->poll_timeout[0]));
+}
+
+/* Load internal firmware from the buffer. If manifest_sync_timeout > 0, use
+ * its value in jiffies in the MANIFEST_SYNC state. */
+static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
+ int manifest_sync_timeout)
+{
+ u8 *block;
+ struct dfu_status dfu_stat_buf;
+ int ret = 0;
+ int need_dfu_state = 1;
+ int is_done = 0;
+ u8 dfu_state = 0;
+ u32 dfu_timeout = 0;
+ int bsize = 0;
+ int blockno = 0;
+
+ at76_dbg(DBG_DFU, "%s( %p, %u, %d)", __func__, buf, size,
+ manifest_sync_timeout);
+
+ if (!size) {
+ dev_printk(KERN_ERR, &udev->dev, "FW buffer length invalid!\n");
+ return -EINVAL;
+ }
+
+ block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL);
+ if (!block)
+ return -ENOMEM;
+
+ do {
+ if (need_dfu_state) {
+ ret = at76_dfu_get_state(udev, &dfu_state);
+ if (ret < 0) {
+ dev_printk(KERN_ERR, &udev->dev,
+ "cannot get DFU state: %d\n", ret);
+ goto exit;
+ }
+ need_dfu_state = 0;
+ }
+
+ switch (dfu_state) {
+ case STATE_DFU_DOWNLOAD_SYNC:
+ at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_SYNC");
+ ret = at76_dfu_get_status(udev, &dfu_stat_buf);
+ if (ret >= 0) {
+ dfu_state = dfu_stat_buf.state;
+ dfu_timeout = at76_get_timeout(&dfu_stat_buf);
+ need_dfu_state = 0;
+ } else
+ dev_printk(KERN_ERR, &udev->dev,
+ "at76_dfu_get_status returned %d\n",
+ ret);
+ break;
+
+ case STATE_DFU_DOWNLOAD_BUSY:
+ at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_BUSY");
+ need_dfu_state = 1;
+
+ at76_dbg(DBG_DFU, "DFU: Resetting device");
+ schedule_timeout_interruptible(dfu_timeout);
+ break;
+
+ case STATE_DFU_DOWNLOAD_IDLE:
+ at76_dbg(DBG_DFU, "DOWNLOAD...");
+ /* fall through */
+ case STATE_DFU_IDLE:
+ at76_dbg(DBG_DFU, "DFU IDLE");
+
+ bsize = min_t(int, size, FW_BLOCK_SIZE);
+ memcpy(block, buf, bsize);
+ at76_dbg(DBG_DFU, "int fw, size left = %5d, "
+ "bsize = %4d, blockno = %2d", size, bsize,
+ blockno);
+ ret =
+ at76_load_int_fw_block(udev, blockno, block, bsize);
+ buf += bsize;
+ size -= bsize;
+ blockno++;
+
+ if (ret != bsize)
+ dev_printk(KERN_ERR, &udev->dev,
+ "at76_load_int_fw_block "
+ "returned %d\n", ret);
+ need_dfu_state = 1;
+ break;
+
+ case STATE_DFU_MANIFEST_SYNC:
+ at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_SYNC");
+
+ ret = at76_dfu_get_status(udev, &dfu_stat_buf);
+ if (ret < 0)
+ break;
+
+ dfu_state = dfu_stat_buf.state;
+ dfu_timeout = at76_get_timeout(&dfu_stat_buf);
+ need_dfu_state = 0;
+
+ /* override the timeout from the status response,
+ needed for AT76C505A */
+ if (manifest_sync_timeout > 0)
+ dfu_timeout = manifest_sync_timeout;
+
+ at76_dbg(DBG_DFU, "DFU: Waiting for manifest phase");
+ schedule_timeout_interruptible(dfu_timeout);
+ break;
+
+ case STATE_DFU_MANIFEST:
+ at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST");
+ is_done = 1;
+ break;
+
+ case STATE_DFU_MANIFEST_WAIT_RESET:
+ at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_WAIT_RESET");
+ is_done = 1;
+ break;
+
+ case STATE_DFU_UPLOAD_IDLE:
+ at76_dbg(DBG_DFU, "STATE_DFU_UPLOAD_IDLE");
+ break;
+
+ case STATE_DFU_ERROR:
+ at76_dbg(DBG_DFU, "STATE_DFU_ERROR");
+ ret = -EPIPE;
+ break;
+
+ default:
+ at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", dfu_state);
+ ret = -EINVAL;
+ break;
+ }
+ } while (!is_done && (ret >= 0));
+
+exit:
+ kfree(block);
+ if (ret >= 0)
+ ret = 0;
+
+ return ret;
+}
+
+#define HEX2STR_BUFFERS 4
+#define HEX2STR_MAX_LEN 64
+
+/* Convert binary data into hex string */
+static char *hex2str(void *buf, size_t len)
+{
+ static atomic_t a = ATOMIC_INIT(0);
+ static char bufs[HEX2STR_BUFFERS][3 * HEX2STR_MAX_LEN + 1];
+ char *ret = bufs[atomic_inc_return(&a) & (HEX2STR_BUFFERS - 1)];
+ char *obuf = ret;
+ u8 *ibuf = buf;
+
+ if (len > HEX2STR_MAX_LEN)
+ len = HEX2STR_MAX_LEN;
+
+ if (len == 0)
+ goto exit;
+
+ while (len--) {
+ obuf = hex_byte_pack(obuf, *ibuf++);
+ *obuf++ = '-';
+ }
+ obuf--;
+
+exit:
+ *obuf = '\0';
+
+ return ret;
+}
+
+/* LED trigger */
+static int tx_activity;
+static void at76_ledtrig_tx_timerfunc(unsigned long data);
+static DEFINE_TIMER(ledtrig_tx_timer, at76_ledtrig_tx_timerfunc, 0, 0);
+DEFINE_LED_TRIGGER(ledtrig_tx);
+
+static void at76_ledtrig_tx_timerfunc(unsigned long data)
+{
+ static int tx_lastactivity;
+
+ if (tx_lastactivity != tx_activity) {
+ tx_lastactivity = tx_activity;
+ led_trigger_event(ledtrig_tx, LED_FULL);
+ mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4);
+ } else
+ led_trigger_event(ledtrig_tx, LED_OFF);
+}
+
+static void at76_ledtrig_tx_activity(void)
+{
+ tx_activity++;
+ if (!timer_pending(&ledtrig_tx_timer))
+ mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4);
+}
+
+static int at76_remap(struct usb_device *udev)
+{
+ int ret;
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0a,
+ USB_TYPE_VENDOR | USB_DIR_OUT |
+ USB_RECIP_INTERFACE, 0, 0, NULL, 0,
+ USB_CTRL_GET_TIMEOUT);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int at76_get_op_mode(struct usb_device *udev)
+{
+ int ret;
+ u8 saved;
+ u8 *op_mode;
+
+ op_mode = kmalloc(1, GFP_NOIO);
+ if (!op_mode)
+ return -ENOMEM;
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33,
+ USB_TYPE_VENDOR | USB_DIR_IN |
+ USB_RECIP_INTERFACE, 0x01, 0, op_mode, 1,
+ USB_CTRL_GET_TIMEOUT);
+ saved = *op_mode;
+ kfree(op_mode);
+
+ if (ret < 0)
+ return ret;
+ else if (ret < 1)
+ return -EIO;
+ else
+ return saved;
+}
+
+/* Load a block of the second ("external") part of the firmware */
+static inline int at76_load_ext_fw_block(struct usb_device *udev, int blockno,
+ void *block, int size)
+{
+ return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e,
+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+ 0x0802, blockno, block, size,
+ USB_CTRL_GET_TIMEOUT);
+}
+
+static inline int at76_get_hw_cfg(struct usb_device *udev,
+ union at76_hwcfg *buf, int buf_size)
+{
+ return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33,
+ USB_TYPE_VENDOR | USB_DIR_IN |
+ USB_RECIP_INTERFACE, 0x0a02, 0,
+ buf, buf_size, USB_CTRL_GET_TIMEOUT);
+}
+
+/* Intersil boards use a different "value" for GetHWConfig requests */
+static inline int at76_get_hw_cfg_intersil(struct usb_device *udev,
+ union at76_hwcfg *buf, int buf_size)
+{
+ return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33,
+ USB_TYPE_VENDOR | USB_DIR_IN |
+ USB_RECIP_INTERFACE, 0x0902, 0,
+ buf, buf_size, USB_CTRL_GET_TIMEOUT);
+}
+
+/* Get the hardware configuration for the adapter and put it to the appropriate
+ * fields of 'priv' (the GetHWConfig request and interpretation of the result
+ * depends on the board type) */
+static int at76_get_hw_config(struct at76_priv *priv)
+{
+ int ret;
+ union at76_hwcfg *hwcfg = kmalloc(sizeof(*hwcfg), GFP_KERNEL);
+
+ if (!hwcfg)
+ return -ENOMEM;
+
+ if (at76_is_intersil(priv->board_type)) {
+ ret = at76_get_hw_cfg_intersil(priv->udev, hwcfg,
+ sizeof(hwcfg->i));
+ if (ret < 0)
+ goto exit;
+ memcpy(priv->mac_addr, hwcfg->i.mac_addr, ETH_ALEN);
+ priv->regulatory_domain = hwcfg->i.regulatory_domain;
+ } else if (at76_is_503rfmd(priv->board_type)) {
+ ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r3));
+ if (ret < 0)
+ goto exit;
+ memcpy(priv->mac_addr, hwcfg->r3.mac_addr, ETH_ALEN);
+ priv->regulatory_domain = hwcfg->r3.regulatory_domain;
+ } else {
+ ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r5));
+ if (ret < 0)
+ goto exit;
+ memcpy(priv->mac_addr, hwcfg->r5.mac_addr, ETH_ALEN);
+ priv->regulatory_domain = hwcfg->r5.regulatory_domain;
+ }
+
+exit:
+ kfree(hwcfg);
+ if (ret < 0)
+ wiphy_err(priv->hw->wiphy, "cannot get HW Config (error %d)\n",
+ ret);
+
+ return ret;
+}
+
+static struct reg_domain const *at76_get_reg_domain(u16 code)
+{
+ int i;
+ static struct reg_domain const fd_tab[] = {
+ { 0x10, "FCC (USA)", 0x7ff }, /* ch 1-11 */
+ { 0x20, "IC (Canada)", 0x7ff }, /* ch 1-11 */
+ { 0x30, "ETSI (most of Europe)", 0x1fff }, /* ch 1-13 */
+ { 0x31, "Spain", 0x600 }, /* ch 10-11 */
+ { 0x32, "France", 0x1e00 }, /* ch 10-13 */
+ { 0x40, "MKK (Japan)", 0x2000 }, /* ch 14 */
+ { 0x41, "MKK1 (Japan)", 0x3fff }, /* ch 1-14 */
+ { 0x50, "Israel", 0x3fc }, /* ch 3-9 */
+ { 0x00, "<unknown>", 0xffffffff } /* ch 1-32 */
+ };
+
+ /* Last entry is fallback for unknown domain code */
+ for (i = 0; i < ARRAY_SIZE(fd_tab) - 1; i++)
+ if (code == fd_tab[i].code)
+ break;
+
+ return &fd_tab[i];
+}
+
+static inline int at76_get_mib(struct usb_device *udev, u16 mib, void *buf,
+ int buf_size)
+{
+ int ret;
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33,
+ USB_TYPE_VENDOR | USB_DIR_IN |
+ USB_RECIP_INTERFACE, mib << 8, 0, buf, buf_size,
+ USB_CTRL_GET_TIMEOUT);
+ if (ret >= 0 && ret != buf_size)
+ return -EIO;
+ return ret;
+}
+
+/* Return positive number for status, negative for an error */
+static inline int at76_get_cmd_status(struct usb_device *udev, u8 cmd)
+{
+ u8 *stat_buf;
+ int ret;
+
+ stat_buf = kmalloc(40, GFP_NOIO);
+ if (!stat_buf)
+ return -ENOMEM;
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x22,
+ USB_TYPE_VENDOR | USB_DIR_IN |
+ USB_RECIP_INTERFACE, cmd, 0, stat_buf,
+ 40, USB_CTRL_GET_TIMEOUT);
+ if (ret >= 0)
+ ret = stat_buf[5];
+ kfree(stat_buf);
+
+ return ret;
+}
+
+#define MAKE_CMD_CASE(c) case (c): return #c
+static const char *at76_get_cmd_string(u8 cmd_status)
+{
+ switch (cmd_status) {
+ MAKE_CMD_CASE(CMD_SET_MIB);
+ MAKE_CMD_CASE(CMD_GET_MIB);
+ MAKE_CMD_CASE(CMD_SCAN);
+ MAKE_CMD_CASE(CMD_JOIN);
+ MAKE_CMD_CASE(CMD_START_IBSS);
+ MAKE_CMD_CASE(CMD_RADIO_ON);
+ MAKE_CMD_CASE(CMD_RADIO_OFF);
+ MAKE_CMD_CASE(CMD_STARTUP);
+ }
+
+ return "UNKNOWN";
+}
+
+static int at76_set_card_command(struct usb_device *udev, u8 cmd, void *buf,
+ int buf_size)
+{
+ int ret;
+ struct at76_command *cmd_buf = kmalloc(sizeof(struct at76_command) +
+ buf_size, GFP_KERNEL);
+
+ if (!cmd_buf)
+ return -ENOMEM;
+
+ cmd_buf->cmd = cmd;
+ cmd_buf->reserved = 0;
+ cmd_buf->size = cpu_to_le16(buf_size);
+ memcpy(cmd_buf->data, buf, buf_size);
+
+ at76_dbg_dump(DBG_CMD, cmd_buf, sizeof(struct at76_command) + buf_size,
+ "issuing command %s (0x%02x)",
+ at76_get_cmd_string(cmd), cmd);
+
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e,
+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+ 0, 0, cmd_buf,
+ sizeof(struct at76_command) + buf_size,
+ USB_CTRL_GET_TIMEOUT);
+ kfree(cmd_buf);
+ return ret;
+}
+
+#define MAKE_CMD_STATUS_CASE(c) case (c): return #c
+static const char *at76_get_cmd_status_string(u8 cmd_status)
+{
+ switch (cmd_status) {
+ MAKE_CMD_STATUS_CASE(CMD_STATUS_IDLE);
+ MAKE_CMD_STATUS_CASE(CMD_STATUS_COMPLETE);
+ MAKE_CMD_STATUS_CASE(CMD_STATUS_UNKNOWN);
+ MAKE_CMD_STATUS_CASE(CMD_STATUS_INVALID_PARAMETER);
+ MAKE_CMD_STATUS_CASE(CMD_STATUS_FUNCTION_NOT_SUPPORTED);
+ MAKE_CMD_STATUS_CASE(CMD_STATUS_TIME_OUT);
+ MAKE_CMD_STATUS_CASE(CMD_STATUS_IN_PROGRESS);
+ MAKE_CMD_STATUS_CASE(CMD_STATUS_HOST_FAILURE);
+ MAKE_CMD_STATUS_CASE(CMD_STATUS_SCAN_FAILED);
+ }
+
+ return "UNKNOWN";
+}
+
+/* Wait until the command is completed */
+static int at76_wait_completion(struct at76_priv *priv, int cmd)
+{
+ int status = 0;
+ unsigned long timeout = jiffies + CMD_COMPLETION_TIMEOUT;
+
+ do {
+ status = at76_get_cmd_status(priv->udev, cmd);
+ if (status < 0) {
+ wiphy_err(priv->hw->wiphy,
+ "at76_get_cmd_status failed: %d\n",
+ status);
+ break;
+ }
+
+ at76_dbg(DBG_WAIT_COMPLETE,
+ "%s: Waiting on cmd %d, status = %d (%s)",
+ wiphy_name(priv->hw->wiphy), cmd, status,
+ at76_get_cmd_status_string(status));
+
+ if (status != CMD_STATUS_IN_PROGRESS
+ && status != CMD_STATUS_IDLE)
+ break;
+
+ schedule_timeout_interruptible(HZ / 10); /* 100 ms */
+ if (time_after(jiffies, timeout)) {
+ wiphy_err(priv->hw->wiphy,
+ "completion timeout for command %d\n", cmd);
+ status = -ETIMEDOUT;
+ break;
+ }
+ } while (1);
+
+ return status;
+}
+
+static int at76_set_mib(struct at76_priv *priv, struct set_mib_buffer *buf)
+{
+ int ret;
+
+ ret = at76_set_card_command(priv->udev, CMD_SET_MIB, buf,
+ offsetof(struct set_mib_buffer,
+ data) + buf->size);
+ if (ret < 0)
+ return ret;
+
+ ret = at76_wait_completion(priv, CMD_SET_MIB);
+ if (ret != CMD_STATUS_COMPLETE) {
+ wiphy_info(priv->hw->wiphy,
+ "set_mib: at76_wait_completion failed with %d\n",
+ ret);
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+/* Return < 0 on error, == 0 if no command sent, == 1 if cmd sent */
+static int at76_set_radio(struct at76_priv *priv, int enable)
+{
+ int ret;
+ int cmd;
+
+ if (priv->radio_on == enable)
+ return 0;
+
+ cmd = enable ? CMD_RADIO_ON : CMD_RADIO_OFF;
+
+ ret = at76_set_card_command(priv->udev, cmd, NULL, 0);
+ if (ret < 0)
+ wiphy_err(priv->hw->wiphy,
+ "at76_set_card_command(%d) failed: %d\n", cmd, ret);
+ else
+ ret = 1;
+
+ priv->radio_on = enable;
+ return ret;
+}
+
+/* Set current power save mode (AT76_PM_OFF/AT76_PM_ON/AT76_PM_SMART) */
+static int at76_set_pm_mode(struct at76_priv *priv)
+{
+ int ret = 0;
+
+ priv->mib_buf.type = MIB_MAC_MGMT;
+ priv->mib_buf.size = 1;
+ priv->mib_buf.index = offsetof(struct mib_mac_mgmt, power_mgmt_mode);
+ priv->mib_buf.data.byte = priv->pm_mode;
+
+ ret = at76_set_mib(priv, &priv->mib_buf);
+ if (ret < 0)
+ wiphy_err(priv->hw->wiphy, "set_mib (pm_mode) failed: %d\n",
+ ret);
+
+ return ret;
+}
+
+static int at76_set_preamble(struct at76_priv *priv, u8 type)
+{
+ int ret = 0;
+
+ priv->mib_buf.type = MIB_LOCAL;
+ priv->mib_buf.size = 1;
+ priv->mib_buf.index = offsetof(struct mib_local, preamble_type);
+ priv->mib_buf.data.byte = type;
+
+ ret = at76_set_mib(priv, &priv->mib_buf);
+ if (ret < 0)
+ wiphy_err(priv->hw->wiphy, "set_mib (preamble) failed: %d\n",
+ ret);
+
+ return ret;
+}
+
+static int at76_set_frag(struct at76_priv *priv, u16 size)
+{
+ int ret = 0;
+
+ priv->mib_buf.type = MIB_MAC;
+ priv->mib_buf.size = 2;
+ priv->mib_buf.index = offsetof(struct mib_mac, frag_threshold);
+ priv->mib_buf.data.word = cpu_to_le16(size);
+
+ ret = at76_set_mib(priv, &priv->mib_buf);
+ if (ret < 0)
+ wiphy_err(priv->hw->wiphy,
+ "set_mib (frag threshold) failed: %d\n", ret);
+
+ return ret;
+}
+
+static int at76_set_rts(struct at76_priv *priv, u16 size)
+{
+ int ret = 0;
+
+ priv->mib_buf.type = MIB_MAC;
+ priv->mib_buf.size = 2;
+ priv->mib_buf.index = offsetof(struct mib_mac, rts_threshold);
+ priv->mib_buf.data.word = cpu_to_le16(size);
+
+ ret = at76_set_mib(priv, &priv->mib_buf);
+ if (ret < 0)
+ wiphy_err(priv->hw->wiphy, "set_mib (rts) failed: %d\n", ret);
+
+ return ret;
+}
+
+static int at76_set_autorate_fallback(struct at76_priv *priv, int onoff)
+{
+ int ret = 0;
+
+ priv->mib_buf.type = MIB_LOCAL;
+ priv->mib_buf.size = 1;
+ priv->mib_buf.index = offsetof(struct mib_local, txautorate_fallback);
+ priv->mib_buf.data.byte = onoff;
+
+ ret = at76_set_mib(priv, &priv->mib_buf);
+ if (ret < 0)
+ wiphy_err(priv->hw->wiphy,
+ "set_mib (autorate fallback) failed: %d\n", ret);
+
+ return ret;
+}
+
+static void at76_dump_mib_mac_addr(struct at76_priv *priv)
+{
+ int i;
+ int ret;
+ struct mib_mac_addr *m = kmalloc(sizeof(struct mib_mac_addr),
+ GFP_KERNEL);
+
+ if (!m)
+ return;
+
+ ret = at76_get_mib(priv->udev, MIB_MAC_ADDR, m,
+ sizeof(struct mib_mac_addr));
+ if (ret < 0) {
+ wiphy_err(priv->hw->wiphy,
+ "at76_get_mib (MAC_ADDR) failed: %d\n", ret);
+ goto exit;
+ }
+
+ at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: mac_addr %pM res 0x%x 0x%x",
+ wiphy_name(priv->hw->wiphy),
+ m->mac_addr, m->res[0], m->res[1]);
+ for (i = 0; i < ARRAY_SIZE(m->group_addr); i++)
+ at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: group addr %d: %pM, "
+ "status %d", wiphy_name(priv->hw->wiphy), i,
+ m->group_addr[i], m->group_addr_status[i]);
+exit:
+ kfree(m);
+}
+
+static void at76_dump_mib_mac_wep(struct at76_priv *priv)
+{
+ int i;
+ int ret;
+ int key_len;
+ struct mib_mac_wep *m = kmalloc(sizeof(struct mib_mac_wep), GFP_KERNEL);
+
+ if (!m)
+ return;
+
+ ret = at76_get_mib(priv->udev, MIB_MAC_WEP, m,
+ sizeof(struct mib_mac_wep));
+ if (ret < 0) {
+ wiphy_err(priv->hw->wiphy,
+ "at76_get_mib (MAC_WEP) failed: %d\n", ret);
+ goto exit;
+ }
+
+ at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: priv_invoked %u def_key_id %u "
+ "key_len %u excl_unencr %u wep_icv_err %u wep_excluded %u "
+ "encr_level %u key %d", wiphy_name(priv->hw->wiphy),
+ m->privacy_invoked, m->wep_default_key_id,
+ m->wep_key_mapping_len, m->exclude_unencrypted,
+ le32_to_cpu(m->wep_icv_error_count),
+ le32_to_cpu(m->wep_excluded_count), m->encryption_level,
+ m->wep_default_key_id);
+
+ key_len = (m->encryption_level == 1) ?
+ WEP_SMALL_KEY_LEN : WEP_LARGE_KEY_LEN;
+
+ for (i = 0; i < WEP_KEYS; i++)
+ at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: key %d: %s",
+ wiphy_name(priv->hw->wiphy), i,
+ hex2str(m->wep_default_keyvalue[i], key_len));
+exit:
+ kfree(m);
+}
+
+static void at76_dump_mib_mac_mgmt(struct at76_priv *priv)
+{
+ int ret;
+ struct mib_mac_mgmt *m = kmalloc(sizeof(struct mib_mac_mgmt),
+ GFP_KERNEL);
+
+ if (!m)
+ return;
+
+ ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, m,
+ sizeof(struct mib_mac_mgmt));
+ if (ret < 0) {
+ wiphy_err(priv->hw->wiphy,
+ "at76_get_mib (MAC_MGMT) failed: %d\n", ret);
+ goto exit;
+ }
+
+ at76_dbg(DBG_MIB, "%s: MIB MAC_MGMT: beacon_period %d CFP_max_duration "
+ "%d medium_occupancy_limit %d station_id 0x%x ATIM_window %d "
+ "CFP_mode %d privacy_opt_impl %d DTIM_period %d CFP_period %d "
+ "current_bssid %pM current_essid %s current_bss_type %d "
+ "pm_mode %d ibss_change %d res %d "
+ "multi_domain_capability_implemented %d "
+ "international_roaming %d country_string %.3s",
+ wiphy_name(priv->hw->wiphy), le16_to_cpu(m->beacon_period),
+ le16_to_cpu(m->CFP_max_duration),
+ le16_to_cpu(m->medium_occupancy_limit),
+ le16_to_cpu(m->station_id), le16_to_cpu(m->ATIM_window),
+ m->CFP_mode, m->privacy_option_implemented, m->DTIM_period,
+ m->CFP_period, m->current_bssid,
+ hex2str(m->current_essid, IW_ESSID_MAX_SIZE),
+ m->current_bss_type, m->power_mgmt_mode, m->ibss_change,
+ m->res, m->multi_domain_capability_implemented,
+ m->multi_domain_capability_enabled, m->country_string);
+exit:
+ kfree(m);
+}
+
+static void at76_dump_mib_mac(struct at76_priv *priv)
+{
+ int ret;
+ struct mib_mac *m = kmalloc(sizeof(struct mib_mac), GFP_KERNEL);
+
+ if (!m)
+ return;
+
+ ret = at76_get_mib(priv->udev, MIB_MAC, m, sizeof(struct mib_mac));
+ if (ret < 0) {
+ wiphy_err(priv->hw->wiphy,
+ "at76_get_mib (MAC) failed: %d\n", ret);
+ goto exit;
+ }
+
+ at76_dbg(DBG_MIB, "%s: MIB MAC: max_tx_msdu_lifetime %d "
+ "max_rx_lifetime %d frag_threshold %d rts_threshold %d "
+ "cwmin %d cwmax %d short_retry_time %d long_retry_time %d "
+ "scan_type %d scan_channel %d probe_delay %u "
+ "min_channel_time %d max_channel_time %d listen_int %d "
+ "desired_ssid %s desired_bssid %pM desired_bsstype %d",
+ wiphy_name(priv->hw->wiphy),
+ le32_to_cpu(m->max_tx_msdu_lifetime),
+ le32_to_cpu(m->max_rx_lifetime),
+ le16_to_cpu(m->frag_threshold), le16_to_cpu(m->rts_threshold),
+ le16_to_cpu(m->cwmin), le16_to_cpu(m->cwmax),
+ m->short_retry_time, m->long_retry_time, m->scan_type,
+ m->scan_channel, le16_to_cpu(m->probe_delay),
+ le16_to_cpu(m->min_channel_time),
+ le16_to_cpu(m->max_channel_time),
+ le16_to_cpu(m->listen_interval),
+ hex2str(m->desired_ssid, IW_ESSID_MAX_SIZE),
+ m->desired_bssid, m->desired_bsstype);
+exit:
+ kfree(m);
+}
+
+static void at76_dump_mib_phy(struct at76_priv *priv)
+{
+ int ret;
+ struct mib_phy *m = kmalloc(sizeof(struct mib_phy), GFP_KERNEL);
+
+ if (!m)
+ return;
+
+ ret = at76_get_mib(priv->udev, MIB_PHY, m, sizeof(struct mib_phy));
+ if (ret < 0) {
+ wiphy_err(priv->hw->wiphy,
+ "at76_get_mib (PHY) failed: %d\n", ret);
+ goto exit;
+ }
+
+ at76_dbg(DBG_MIB, "%s: MIB PHY: ed_threshold %d slot_time %d "
+ "sifs_time %d preamble_length %d plcp_header_length %d "
+ "mpdu_max_length %d cca_mode_supported %d operation_rate_set "
+ "0x%x 0x%x 0x%x 0x%x channel_id %d current_cca_mode %d "
+ "phy_type %d current_reg_domain %d",
+ wiphy_name(priv->hw->wiphy), le32_to_cpu(m->ed_threshold),
+ le16_to_cpu(m->slot_time), le16_to_cpu(m->sifs_time),
+ le16_to_cpu(m->preamble_length),
+ le16_to_cpu(m->plcp_header_length),
+ le16_to_cpu(m->mpdu_max_length),
+ le16_to_cpu(m->cca_mode_supported), m->operation_rate_set[0],
+ m->operation_rate_set[1], m->operation_rate_set[2],
+ m->operation_rate_set[3], m->channel_id, m->current_cca_mode,
+ m->phy_type, m->current_reg_domain);
+exit:
+ kfree(m);
+}
+
+static void at76_dump_mib_local(struct at76_priv *priv)
+{
+ int ret;
+ struct mib_local *m = kmalloc(sizeof(struct mib_phy), GFP_KERNEL);
+
+ if (!m)
+ return;
+
+ ret = at76_get_mib(priv->udev, MIB_LOCAL, m, sizeof(struct mib_local));
+ if (ret < 0) {
+ wiphy_err(priv->hw->wiphy,
+ "at76_get_mib (LOCAL) failed: %d\n", ret);
+ goto exit;
+ }
+
+ at76_dbg(DBG_MIB, "%s: MIB LOCAL: beacon_enable %d "
+ "txautorate_fallback %d ssid_size %d promiscuous_mode %d "
+ "preamble_type %d", wiphy_name(priv->hw->wiphy),
+ m->beacon_enable,
+ m->txautorate_fallback, m->ssid_size, m->promiscuous_mode,
+ m->preamble_type);
+exit:
+ kfree(m);
+}
+
+static void at76_dump_mib_mdomain(struct at76_priv *priv)
+{
+ int ret;
+ struct mib_mdomain *m = kmalloc(sizeof(struct mib_mdomain), GFP_KERNEL);
+
+ if (!m)
+ return;
+
+ ret = at76_get_mib(priv->udev, MIB_MDOMAIN, m,
+ sizeof(struct mib_mdomain));
+ if (ret < 0) {
+ wiphy_err(priv->hw->wiphy,
+ "at76_get_mib (MDOMAIN) failed: %d\n", ret);
+ goto exit;
+ }
+
+ at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: channel_list %s",
+ wiphy_name(priv->hw->wiphy),
+ hex2str(m->channel_list, sizeof(m->channel_list)));
+
+ at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: tx_powerlevel %s",
+ wiphy_name(priv->hw->wiphy),
+ hex2str(m->tx_powerlevel, sizeof(m->tx_powerlevel)));
+exit:
+ kfree(m);
+}
+
+/* Enable monitor mode */
+static int at76_start_monitor(struct at76_priv *priv)
+{
+ struct at76_req_scan scan;
+ int ret;
+
+ memset(&scan, 0, sizeof(struct at76_req_scan));
+ memset(scan.bssid, 0xff, ETH_ALEN);
+
+ scan.channel = priv->channel;
+ scan.scan_type = SCAN_TYPE_PASSIVE;
+ scan.international_scan = 0;
+ scan.min_channel_time = cpu_to_le16(priv->scan_min_time);
+ scan.max_channel_time = cpu_to_le16(priv->scan_max_time);
+ scan.probe_delay = cpu_to_le16(0);
+
+ ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan));
+ if (ret >= 0)
+ ret = at76_get_cmd_status(priv->udev, CMD_SCAN);
+
+ return ret;
+}
+
+/* Calculate padding from txbuf->wlength (which excludes the USB TX header),
+ likely to compensate a flaw in the AT76C503A USB part ... */
+static inline int at76_calc_padding(int wlen)
+{
+ /* add the USB TX header */
+ wlen += AT76_TX_HDRLEN;
+
+ wlen = wlen % 64;
+
+ if (wlen < 50)
+ return 50 - wlen;
+
+ if (wlen >= 61)
+ return 64 + 50 - wlen;
+
+ return 0;
+}
+
+static void at76_rx_callback(struct urb *urb)
+{
+ struct at76_priv *priv = urb->context;
+
+ priv->rx_tasklet.data = (unsigned long)urb;
+ tasklet_schedule(&priv->rx_tasklet);
+}
+
+static int at76_submit_rx_urb(struct at76_priv *priv)
+{
+ int ret;
+ int size;
+ struct sk_buff *skb = priv->rx_skb;
+
+ if (!priv->rx_urb) {
+ wiphy_err(priv->hw->wiphy, "%s: priv->rx_urb is NULL\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ if (!skb) {
+ skb = dev_alloc_skb(sizeof(struct at76_rx_buffer));
+ if (!skb) {
+ wiphy_err(priv->hw->wiphy,
+ "cannot allocate rx skbuff\n");
+ ret = -ENOMEM;
+ goto exit;
+ }
+ priv->rx_skb = skb;
+ } else {
+ skb_push(skb, skb_headroom(skb));
+ skb_trim(skb, 0);
+ }
+
+ size = skb_tailroom(skb);
+ usb_fill_bulk_urb(priv->rx_urb, priv->udev, priv->rx_pipe,
+ skb_put(skb, size), size, at76_rx_callback, priv);
+ ret = usb_submit_urb(priv->rx_urb, GFP_ATOMIC);
+ if (ret < 0) {
+ if (ret == -ENODEV)
+ at76_dbg(DBG_DEVSTART,
+ "usb_submit_urb returned -ENODEV");
+ else
+ wiphy_err(priv->hw->wiphy,
+ "rx, usb_submit_urb failed: %d\n", ret);
+ }
+
+exit:
+ if (ret < 0 && ret != -ENODEV)
+ wiphy_err(priv->hw->wiphy,
+ "cannot submit rx urb - please unload the driver and/or power cycle the device\n");
+
+ return ret;
+}
+
+/* Download external firmware */
+static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe)
+{
+ int ret;
+ int op_mode;
+ int blockno = 0;
+ int bsize;
+ u8 *block;
+ u8 *buf = fwe->extfw;
+ int size = fwe->extfw_size;
+
+ if (!buf || !size)
+ return -ENOENT;
+
+ op_mode = at76_get_op_mode(udev);
+ at76_dbg(DBG_DEVSTART, "opmode %d", op_mode);
+
+ if (op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) {
+ dev_printk(KERN_ERR, &udev->dev, "unexpected opmode %d\n",
+ op_mode);
+ return -EINVAL;
+ }
+
+ block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL);
+ if (!block)
+ return -ENOMEM;
+
+ at76_dbg(DBG_DEVSTART, "downloading external firmware");
+
+ /* for fw >= 0.100, the device needs an extra empty block */
+ do {
+ bsize = min_t(int, size, FW_BLOCK_SIZE);
+ memcpy(block, buf, bsize);
+ at76_dbg(DBG_DEVSTART,
+ "ext fw, size left = %5d, bsize = %4d, blockno = %2d",
+ size, bsize, blockno);
+ ret = at76_load_ext_fw_block(udev, blockno, block, bsize);
+ if (ret != bsize) {
+ dev_printk(KERN_ERR, &udev->dev,
+ "loading %dth firmware block failed: %d\n",
+ blockno, ret);
+ goto exit;
+ }
+ buf += bsize;
+ size -= bsize;
+ blockno++;
+ } while (bsize > 0);
+
+ if (at76_is_505a(fwe->board_type)) {
+ at76_dbg(DBG_DEVSTART, "200 ms delay for 505a");
+ schedule_timeout_interruptible(HZ / 5 + 1);
+ }
+
+exit:
+ kfree(block);
+ if (ret < 0)
+ dev_printk(KERN_ERR, &udev->dev,
+ "downloading external firmware failed: %d\n", ret);
+ return ret;
+}
+
+/* Download internal firmware */
+static int at76_load_internal_fw(struct usb_device *udev, struct fwentry *fwe)
+{
+ int ret;
+ int need_remap = !at76_is_505a(fwe->board_type);
+
+ ret = at76_usbdfu_download(udev, fwe->intfw, fwe->intfw_size,
+ need_remap ? 0 : 2 * HZ);
+
+ if (ret < 0) {
+ dev_printk(KERN_ERR, &udev->dev,
+ "downloading internal fw failed with %d\n", ret);
+ goto exit;
+ }
+
+ at76_dbg(DBG_DEVSTART, "sending REMAP");
+
+ /* no REMAP for 505A (see SF driver) */
+ if (need_remap) {
+ ret = at76_remap(udev);
+ if (ret < 0) {
+ dev_printk(KERN_ERR, &udev->dev,
+ "sending REMAP failed with %d\n", ret);
+ goto exit;
+ }
+ }
+
+ at76_dbg(DBG_DEVSTART, "sleeping for 2 seconds");
+ schedule_timeout_interruptible(2 * HZ + 1);
+ usb_reset_device(udev);
+
+exit:
+ return ret;
+}
+
+static int at76_startup_device(struct at76_priv *priv)
+{
+ struct at76_card_config *ccfg = &priv->card_config;
+ int ret;
+
+ at76_dbg(DBG_PARAMS,
+ "%s param: ssid %.*s (%s) mode %s ch %d wep %s key %d "
+ "keylen %d", wiphy_name(priv->hw->wiphy), priv->essid_size,
+ priv->essid, hex2str(priv->essid, IW_ESSID_MAX_SIZE),
+ priv->iw_mode == IW_MODE_ADHOC ? "adhoc" : "infra",
+ priv->channel, priv->wep_enabled ? "enabled" : "disabled",
+ priv->wep_key_id, priv->wep_keys_len[priv->wep_key_id]);
+ at76_dbg(DBG_PARAMS,
+ "%s param: preamble %s rts %d retry %d frag %d "
+ "txrate %s auth_mode %d", wiphy_name(priv->hw->wiphy),
+ preambles[priv->preamble_type], priv->rts_threshold,
+ priv->short_retry_limit, priv->frag_threshold,
+ priv->txrate == TX_RATE_1MBIT ? "1MBit" : priv->txrate ==
+ TX_RATE_2MBIT ? "2MBit" : priv->txrate ==
+ TX_RATE_5_5MBIT ? "5.5MBit" : priv->txrate ==
+ TX_RATE_11MBIT ? "11MBit" : priv->txrate ==
+ TX_RATE_AUTO ? "auto" : "<invalid>", priv->auth_mode);
+ at76_dbg(DBG_PARAMS,
+ "%s param: pm_mode %d pm_period %d auth_mode %s "
+ "scan_times %d %d scan_mode %s",
+ wiphy_name(priv->hw->wiphy), priv->pm_mode, priv->pm_period,
+ priv->auth_mode == WLAN_AUTH_OPEN ? "open" : "shared_secret",
+ priv->scan_min_time, priv->scan_max_time,
+ priv->scan_mode == SCAN_TYPE_ACTIVE ? "active" : "passive");
+
+ memset(ccfg, 0, sizeof(struct at76_card_config));
+ ccfg->promiscuous_mode = 0;
+ ccfg->short_retry_limit = priv->short_retry_limit;
+
+ if (priv->wep_enabled) {
+ if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN)
+ ccfg->encryption_type = 2;
+ else
+ ccfg->encryption_type = 1;
+
+ /* jal: always exclude unencrypted if WEP is active */
+ ccfg->exclude_unencrypted = 1;
+ } else {
+ ccfg->exclude_unencrypted = 0;
+ ccfg->encryption_type = 0;
+ }
+
+ ccfg->rts_threshold = cpu_to_le16(priv->rts_threshold);
+ ccfg->fragmentation_threshold = cpu_to_le16(priv->frag_threshold);
+
+ memcpy(ccfg->basic_rate_set, hw_rates, 4);
+ /* jal: really needed, we do a set_mib for autorate later ??? */
+ ccfg->auto_rate_fallback = (priv->txrate == TX_RATE_AUTO ? 1 : 0);
+ ccfg->channel = priv->channel;
+ ccfg->privacy_invoked = priv->wep_enabled;
+ memcpy(ccfg->current_ssid, priv->essid, IW_ESSID_MAX_SIZE);
+ ccfg->ssid_len = priv->essid_size;
+
+ ccfg->wep_default_key_id = priv->wep_key_id;
+ memcpy(ccfg->wep_default_key_value, priv->wep_keys,
+ sizeof(priv->wep_keys));
+
+ ccfg->short_preamble = priv->preamble_type;
+ ccfg->beacon_period = cpu_to_le16(priv->beacon_period);
+
+ ret = at76_set_card_command(priv->udev, CMD_STARTUP, &priv->card_config,
+ sizeof(struct at76_card_config));
+ if (ret < 0) {
+ wiphy_err(priv->hw->wiphy, "at76_set_card_command failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ at76_wait_completion(priv, CMD_STARTUP);
+
+ /* remove BSSID from previous run */
+ memset(priv->bssid, 0, ETH_ALEN);
+
+ if (at76_set_radio(priv, 1) == 1)
+ at76_wait_completion(priv, CMD_RADIO_ON);
+
+ ret = at76_set_preamble(priv, priv->preamble_type);
+ if (ret < 0)
+ return ret;
+
+ ret = at76_set_frag(priv, priv->frag_threshold);
+ if (ret < 0)
+ return ret;
+
+ ret = at76_set_rts(priv, priv->rts_threshold);
+ if (ret < 0)
+ return ret;
+
+ ret = at76_set_autorate_fallback(priv,
+ priv->txrate == TX_RATE_AUTO ? 1 : 0);
+ if (ret < 0)
+ return ret;
+
+ ret = at76_set_pm_mode(priv);
+ if (ret < 0)
+ return ret;
+
+ if (at76_debug & DBG_MIB) {
+ at76_dump_mib_mac(priv);
+ at76_dump_mib_mac_addr(priv);
+ at76_dump_mib_mac_mgmt(priv);
+ at76_dump_mib_mac_wep(priv);
+ at76_dump_mib_mdomain(priv);
+ at76_dump_mib_phy(priv);
+ at76_dump_mib_local(priv);
+ }
+
+ return 0;
+}
+
+/* Enable or disable promiscuous mode */
+static void at76_work_set_promisc(struct work_struct *work)
+{
+ struct at76_priv *priv = container_of(work, struct at76_priv,
+ work_set_promisc);
+ int ret = 0;
+
+ if (priv->device_unplugged)
+ return;
+
+ mutex_lock(&priv->mtx);
+
+ priv->mib_buf.type = MIB_LOCAL;
+ priv->mib_buf.size = 1;
+ priv->mib_buf.index = offsetof(struct mib_local, promiscuous_mode);
+ priv->mib_buf.data.byte = priv->promisc ? 1 : 0;
+
+ ret = at76_set_mib(priv, &priv->mib_buf);
+ if (ret < 0)
+ wiphy_err(priv->hw->wiphy,
+ "set_mib (promiscuous_mode) failed: %d\n", ret);
+
+ mutex_unlock(&priv->mtx);
+}
+
+/* Submit Rx urb back to the device */
+static void at76_work_submit_rx(struct work_struct *work)
+{
+ struct at76_priv *priv = container_of(work, struct at76_priv,
+ work_submit_rx);
+
+ mutex_lock(&priv->mtx);
+ at76_submit_rx_urb(priv);
+ mutex_unlock(&priv->mtx);
+}
+
+static void at76_rx_tasklet(unsigned long param)
+{
+ struct urb *urb = (struct urb *)param;
+ struct at76_priv *priv = urb->context;
+ struct at76_rx_buffer *buf;
+ struct ieee80211_rx_status rx_status = { 0 };
+
+ if (priv->device_unplugged) {
+ at76_dbg(DBG_DEVSTART, "device unplugged");
+ at76_dbg(DBG_DEVSTART, "urb status %d", urb->status);
+ return;
+ }
+
+ if (!priv->rx_skb || !priv->rx_skb->data)
+ return;
+
+ buf = (struct at76_rx_buffer *)priv->rx_skb->data;
+
+ if (urb->status != 0) {
+ if (urb->status != -ENOENT && urb->status != -ECONNRESET)
+ at76_dbg(DBG_URB,
+ "%s %s: - nonzero Rx bulk status received: %d",
+ __func__, wiphy_name(priv->hw->wiphy),
+ urb->status);
+ return;
+ }
+
+ at76_dbg(DBG_RX_ATMEL_HDR,
+ "%s: rx frame: rate %d rssi %d noise %d link %d",
+ wiphy_name(priv->hw->wiphy), buf->rx_rate, buf->rssi,
+ buf->noise_level, buf->link_quality);
+
+ skb_pull(priv->rx_skb, AT76_RX_HDRLEN);
+ skb_trim(priv->rx_skb, le16_to_cpu(buf->wlength));
+ at76_dbg_dump(DBG_RX_DATA, priv->rx_skb->data,
+ priv->rx_skb->len, "RX: len=%d", priv->rx_skb->len);
+
+ rx_status.signal = buf->rssi;
+ rx_status.flag |= RX_FLAG_DECRYPTED;
+ rx_status.flag |= RX_FLAG_IV_STRIPPED;
+
+ at76_dbg(DBG_MAC80211, "calling ieee80211_rx_irqsafe(): %d/%d",
+ priv->rx_skb->len, priv->rx_skb->data_len);
+ memcpy(IEEE80211_SKB_RXCB(priv->rx_skb), &rx_status, sizeof(rx_status));
+ ieee80211_rx_irqsafe(priv->hw, priv->rx_skb);
+
+ /* Use a new skb for the next receive */
+ priv->rx_skb = NULL;
+
+ at76_submit_rx_urb(priv);
+}
+
+/* Load firmware into kernel memory and parse it */
+static struct fwentry *at76_load_firmware(struct usb_device *udev,
+ enum board_type board_type)
+{
+ int ret;
+ char *str;
+ struct at76_fw_header *fwh;
+ struct fwentry *fwe = &firmwares[board_type];
+
+ mutex_lock(&fw_mutex);
+
+ if (fwe->loaded) {
+ at76_dbg(DBG_FW, "re-using previously loaded fw");
+ goto exit;
+ }
+
+ at76_dbg(DBG_FW, "downloading firmware %s", fwe->fwname);
+ ret = request_firmware(&fwe->fw, fwe->fwname, &udev->dev);
+ if (ret < 0) {
+ dev_printk(KERN_ERR, &udev->dev, "firmware %s not found!\n",
+ fwe->fwname);
+ dev_printk(KERN_ERR, &udev->dev,
+ "you may need to download the firmware from "
+ "http://developer.berlios.de/projects/at76c503a/\n");
+ goto exit;
+ }
+
+ at76_dbg(DBG_FW, "got it.");
+ fwh = (struct at76_fw_header *)(fwe->fw->data);
+
+ if (fwe->fw->size <= sizeof(*fwh)) {
+ dev_printk(KERN_ERR, &udev->dev,
+ "firmware is too short (0x%zx)\n", fwe->fw->size);
+ goto exit;
+ }
+
+ /* CRC currently not checked */
+ fwe->board_type = le32_to_cpu(fwh->board_type);
+ if (fwe->board_type != board_type) {
+ dev_printk(KERN_ERR, &udev->dev,
+ "board type mismatch, requested %u, got %u\n",
+ board_type, fwe->board_type);
+ goto exit;
+ }
+
+ fwe->fw_version.major = fwh->major;
+ fwe->fw_version.minor = fwh->minor;
+ fwe->fw_version.patch = fwh->patch;
+ fwe->fw_version.build = fwh->build;
+
+ str = (char *)fwh + le32_to_cpu(fwh->str_offset);
+ fwe->intfw = (u8 *)fwh + le32_to_cpu(fwh->int_fw_offset);
+ fwe->intfw_size = le32_to_cpu(fwh->int_fw_len);
+ fwe->extfw = (u8 *)fwh + le32_to_cpu(fwh->ext_fw_offset);
+ fwe->extfw_size = le32_to_cpu(fwh->ext_fw_len);
+
+ fwe->loaded = 1;
+
+ dev_printk(KERN_DEBUG, &udev->dev,
+ "using firmware %s (version %d.%d.%d-%d)\n",
+ fwe->fwname, fwh->major, fwh->minor, fwh->patch, fwh->build);
+
+ at76_dbg(DBG_DEVSTART, "board %u, int %d:%d, ext %d:%d", board_type,
+ le32_to_cpu(fwh->int_fw_offset), le32_to_cpu(fwh->int_fw_len),
+ le32_to_cpu(fwh->ext_fw_offset), le32_to_cpu(fwh->ext_fw_len));
+ at76_dbg(DBG_DEVSTART, "firmware id %s", str);
+
+exit:
+ mutex_unlock(&fw_mutex);
+
+ if (fwe->loaded)
+ return fwe;
+ else
+ return NULL;
+}
+
+static int at76_join(struct at76_priv *priv)
+{
+ struct at76_req_join join;
+ int ret;
+
+ memset(&join, 0, sizeof(struct at76_req_join));
+ memcpy(join.essid, priv->essid, priv->essid_size);
+ join.essid_size = priv->essid_size;
+ memcpy(join.bssid, priv->bssid, ETH_ALEN);
+ join.bss_type = INFRASTRUCTURE_MODE;
+ join.channel = priv->channel;
+ join.timeout = cpu_to_le16(2000);
+
+ at76_dbg(DBG_MAC80211, "%s: sending CMD_JOIN", __func__);
+ ret = at76_set_card_command(priv->udev, CMD_JOIN, &join,
+ sizeof(struct at76_req_join));
+
+ if (ret < 0) {
+ wiphy_err(priv->hw->wiphy, "at76_set_card_command failed: %d\n",
+ ret);
+ return 0;
+ }
+
+ ret = at76_wait_completion(priv, CMD_JOIN);
+ at76_dbg(DBG_MAC80211, "%s: CMD_JOIN returned: 0x%02x", __func__, ret);
+ if (ret != CMD_STATUS_COMPLETE) {
+ wiphy_err(priv->hw->wiphy, "at76_wait_completion failed: %d\n",
+ ret);
+ return 0;
+ }
+
+ at76_set_pm_mode(priv);
+
+ return 0;
+}
+
+static void at76_work_join_bssid(struct work_struct *work)
+{
+ struct at76_priv *priv = container_of(work, struct at76_priv,
+ work_join_bssid);
+
+ if (priv->device_unplugged)
+ return;
+
+ mutex_lock(&priv->mtx);
+
+ if (is_valid_ether_addr(priv->bssid))
+ at76_join(priv);
+
+ mutex_unlock(&priv->mtx);
+}
+
+static void at76_mac80211_tx_callback(struct urb *urb)
+{
+ struct at76_priv *priv = urb->context;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb);
+
+ at76_dbg(DBG_MAC80211, "%s()", __func__);
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ break;
+ case -ENOENT:
+ case -ECONNRESET:
+ /* fail, urb has been unlinked */
+ /* FIXME: add error message */
+ break;
+ default:
+ at76_dbg(DBG_URB, "%s - nonzero tx status received: %d",
+ __func__, urb->status);
+ break;
+ }
+
+ memset(&info->status, 0, sizeof(info->status));
+
+ ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb);
+
+ priv->tx_skb = NULL;
+
+ ieee80211_wake_queues(priv->hw);
+}
+
+static void at76_mac80211_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ struct at76_priv *priv = hw->priv;
+ struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+ int padding, submit_len, ret;
+
+ at76_dbg(DBG_MAC80211, "%s()", __func__);
+
+ if (priv->tx_urb->status == -EINPROGRESS) {
+ wiphy_err(priv->hw->wiphy,
+ "%s called while tx urb is pending\n", __func__);
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
+ /* The following code lines are important when the device is going to
+ * authenticate with a new bssid. The driver must send CMD_JOIN before
+ * an authentication frame is transmitted. For this to succeed, the
+ * correct bssid of the AP must be known. As mac80211 does not inform
+ * drivers about the bssid prior to the authentication process the
+ * following workaround is necessary. If the TX frame is an
+ * authentication frame extract the bssid and send the CMD_JOIN. */
+ if (mgmt->frame_control & cpu_to_le16(IEEE80211_STYPE_AUTH)) {
+ if (compare_ether_addr(priv->bssid, mgmt->bssid)) {
+ memcpy(priv->bssid, mgmt->bssid, ETH_ALEN);
+ ieee80211_queue_work(hw, &priv->work_join_bssid);
+ dev_kfree_skb_any(skb);
+ return;
+ }
+ }
+
+ ieee80211_stop_queues(hw);
+
+ at76_ledtrig_tx_activity(); /* tell ledtrigger we send a packet */
+
+ WARN_ON(priv->tx_skb != NULL);
+
+ priv->tx_skb = skb;
+ padding = at76_calc_padding(skb->len);
+ submit_len = AT76_TX_HDRLEN + skb->len + padding;
+
+ /* setup 'Atmel' header */
+ memset(tx_buffer, 0, sizeof(*tx_buffer));
+ tx_buffer->padding = padding;
+ tx_buffer->wlength = cpu_to_le16(skb->len);
+ tx_buffer->tx_rate = ieee80211_get_tx_rate(hw, info)->hw_value;
+ memset(tx_buffer->reserved, 0, sizeof(tx_buffer->reserved));
+ memcpy(tx_buffer->packet, skb->data, skb->len);
+
+ at76_dbg(DBG_TX_DATA, "%s tx: wlen 0x%x pad 0x%x rate %d hdr",
+ wiphy_name(priv->hw->wiphy), le16_to_cpu(tx_buffer->wlength),
+ tx_buffer->padding, tx_buffer->tx_rate);
+
+ /* send stuff */
+ at76_dbg_dump(DBG_TX_DATA_CONTENT, tx_buffer, submit_len,
+ "%s(): tx_buffer %d bytes:", __func__, submit_len);
+ usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, tx_buffer,
+ submit_len, at76_mac80211_tx_callback, priv);
+ ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC);
+ if (ret) {
+ wiphy_err(priv->hw->wiphy, "error in tx submit urb: %d\n", ret);
+ if (ret == -EINVAL)
+ wiphy_err(priv->hw->wiphy,
+ "-EINVAL: tx urb %p hcpriv %p complete %p\n",
+ priv->tx_urb,
+ priv->tx_urb->hcpriv, priv->tx_urb->complete);
+ }
+}
+
+static int at76_mac80211_start(struct ieee80211_hw *hw)
+{
+ struct at76_priv *priv = hw->priv;
+ int ret;
+
+ at76_dbg(DBG_MAC80211, "%s()", __func__);
+
+ mutex_lock(&priv->mtx);
+
+ ret = at76_submit_rx_urb(priv);
+ if (ret < 0) {
+ wiphy_err(priv->hw->wiphy, "open: submit_rx_urb failed: %d\n",
+ ret);
+ goto error;
+ }
+
+ at76_startup_device(priv);
+
+ at76_start_monitor(priv);
+
+error:
+ mutex_unlock(&priv->mtx);
+
+ return 0;
+}
+
+static void at76_mac80211_stop(struct ieee80211_hw *hw)
+{
+ struct at76_priv *priv = hw->priv;
+
+ at76_dbg(DBG_MAC80211, "%s()", __func__);
+
+ cancel_delayed_work(&priv->dwork_hw_scan);
+ cancel_work_sync(&priv->work_join_bssid);
+ cancel_work_sync(&priv->work_set_promisc);
+
+ mutex_lock(&priv->mtx);
+
+ if (!priv->device_unplugged) {
+ /* We are called by "ifconfig ethX down", not because the
+ * device is not available anymore. */
+ at76_set_radio(priv, 0);
+
+ /* We unlink rx_urb because at76_open() re-submits it.
+ * If unplugged, at76_delete_device() takes care of it. */
+ usb_kill_urb(priv->rx_urb);
+ }
+
+ mutex_unlock(&priv->mtx);
+}
+
+static int at76_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct at76_priv *priv = hw->priv;
+ int ret = 0;
+
+ at76_dbg(DBG_MAC80211, "%s()", __func__);
+
+ mutex_lock(&priv->mtx);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ priv->iw_mode = IW_MODE_INFRA;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ goto exit;
+ }
+
+exit:
+ mutex_unlock(&priv->mtx);
+
+ return ret;
+}
+
+static void at76_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ at76_dbg(DBG_MAC80211, "%s()", __func__);
+}
+
+static void at76_dwork_hw_scan(struct work_struct *work)
+{
+ struct at76_priv *priv = container_of(work, struct at76_priv,
+ dwork_hw_scan.work);
+ int ret;
+
+ if (priv->device_unplugged)
+ return;
+
+ mutex_lock(&priv->mtx);
+
+ ret = at76_get_cmd_status(priv->udev, CMD_SCAN);
+ at76_dbg(DBG_MAC80211, "%s: CMD_SCAN status 0x%02x", __func__, ret);
+
+ /* FIXME: add maximum time for scan to complete */
+
+ if (ret != CMD_STATUS_COMPLETE) {
+ ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan,
+ SCAN_POLL_INTERVAL);
+ mutex_unlock(&priv->mtx);
+ return;
+ }
+
+ if (is_valid_ether_addr(priv->bssid))
+ at76_join(priv);
+
+ mutex_unlock(&priv->mtx);
+
+ ieee80211_scan_completed(priv->hw, false);
+
+ ieee80211_wake_queues(priv->hw);
+}
+
+static int at76_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_scan_request *req)
+{
+ struct at76_priv *priv = hw->priv;
+ struct at76_req_scan scan;
+ u8 *ssid = NULL;
+ int ret, len = 0;
+
+ at76_dbg(DBG_MAC80211, "%s():", __func__);
+
+ if (priv->device_unplugged)
+ return 0;
+
+ mutex_lock(&priv->mtx);
+
+ ieee80211_stop_queues(hw);
+
+ memset(&scan, 0, sizeof(struct at76_req_scan));
+ memset(scan.bssid, 0xFF, ETH_ALEN);
+
+ if (req->n_ssids) {
+ scan.scan_type = SCAN_TYPE_ACTIVE;
+ ssid = req->ssids[0].ssid;
+ len = req->ssids[0].ssid_len;
+ } else {
+ scan.scan_type = SCAN_TYPE_PASSIVE;
+ }
+
+ if (len) {
+ memcpy(scan.essid, ssid, len);
+ scan.essid_size = len;
+ }
+
+ scan.min_channel_time = cpu_to_le16(priv->scan_min_time);
+ scan.max_channel_time = cpu_to_le16(priv->scan_max_time);
+ scan.probe_delay = cpu_to_le16(priv->scan_min_time * 1000);
+ scan.international_scan = 0;
+
+ at76_dbg(DBG_MAC80211, "%s: sending CMD_SCAN", __func__);
+ ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan));
+
+ if (ret < 0) {
+ err("CMD_SCAN failed: %d", ret);
+ goto exit;
+ }
+
+ ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan,
+ SCAN_POLL_INTERVAL);
+
+exit:
+ mutex_unlock(&priv->mtx);
+
+ return 0;
+}
+
+static int at76_config(struct ieee80211_hw *hw, u32 changed)
+{
+ struct at76_priv *priv = hw->priv;
+
+ at76_dbg(DBG_MAC80211, "%s(): channel %d",
+ __func__, hw->conf.channel->hw_value);
+ at76_dbg_dump(DBG_MAC80211, priv->bssid, ETH_ALEN, "bssid:");
+
+ mutex_lock(&priv->mtx);
+
+ priv->channel = hw->conf.channel->hw_value;
+
+ if (is_valid_ether_addr(priv->bssid))
+ at76_join(priv);
+ else
+ at76_start_monitor(priv);
+
+ mutex_unlock(&priv->mtx);
+
+ return 0;
+}
+
+static void at76_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *conf,
+ u32 changed)
+{
+ struct at76_priv *priv = hw->priv;
+
+ at76_dbg(DBG_MAC80211, "%s():", __func__);
+
+ if (!(changed & BSS_CHANGED_BSSID))
+ return;
+
+ at76_dbg_dump(DBG_MAC80211, conf->bssid, ETH_ALEN, "bssid:");
+
+ mutex_lock(&priv->mtx);
+
+ memcpy(priv->bssid, conf->bssid, ETH_ALEN);
+
+ if (is_valid_ether_addr(priv->bssid))
+ /* mac80211 is joining a bss */
+ at76_join(priv);
+
+ mutex_unlock(&priv->mtx);
+}
+
+/* must be atomic */
+static void at76_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *total_flags, u64 multicast)
+{
+ struct at76_priv *priv = hw->priv;
+ int flags;
+
+ at76_dbg(DBG_MAC80211, "%s(): changed_flags=0x%08x "
+ "total_flags=0x%08x",
+ __func__, changed_flags, *total_flags);
+
+ flags = changed_flags & AT76_SUPPORTED_FILTERS;
+ *total_flags = AT76_SUPPORTED_FILTERS;
+
+ /* Bail out after updating flags to prevent a WARN_ON in mac80211. */
+ if (priv->device_unplugged)
+ return;
+
+ /* FIXME: access to priv->promisc should be protected with
+ * priv->mtx, but it's impossible because this function needs to be
+ * atomic */
+
+ if (flags && !priv->promisc) {
+ /* mac80211 wants us to enable promiscuous mode */
+ priv->promisc = 1;
+ } else if (!flags && priv->promisc) {
+ /* we need to disable promiscuous mode */
+ priv->promisc = 0;
+ } else
+ return;
+
+ ieee80211_queue_work(hw, &priv->work_set_promisc);
+}
+
+static int at76_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 at76_priv *priv = hw->priv;
+
+ int i;
+
+ at76_dbg(DBG_MAC80211, "%s(): cmd %d key->cipher %d key->keyidx %d "
+ "key->keylen %d",
+ __func__, cmd, key->cipher, key->keyidx, key->keylen);
+
+ if ((key->cipher != WLAN_CIPHER_SUITE_WEP40) &&
+ (key->cipher != WLAN_CIPHER_SUITE_WEP104))
+ return -EOPNOTSUPP;
+
+ key->hw_key_idx = key->keyidx;
+
+ mutex_lock(&priv->mtx);
+
+ switch (cmd) {
+ case SET_KEY:
+ memcpy(priv->wep_keys[key->keyidx], key->key, key->keylen);
+ priv->wep_keys_len[key->keyidx] = key->keylen;
+
+ /* FIXME: find out how to do this properly */
+ priv->wep_key_id = key->keyidx;
+
+ break;
+ case DISABLE_KEY:
+ default:
+ priv->wep_keys_len[key->keyidx] = 0;
+ break;
+ }
+
+ priv->wep_enabled = 0;
+
+ for (i = 0; i < WEP_KEYS; i++) {
+ if (priv->wep_keys_len[i] != 0)
+ priv->wep_enabled = 1;
+ }
+
+ at76_startup_device(priv);
+
+ mutex_unlock(&priv->mtx);
+
+ return 0;
+}
+
+static const struct ieee80211_ops at76_ops = {
+ .tx = at76_mac80211_tx,
+ .add_interface = at76_add_interface,
+ .remove_interface = at76_remove_interface,
+ .config = at76_config,
+ .bss_info_changed = at76_bss_info_changed,
+ .configure_filter = at76_configure_filter,
+ .start = at76_mac80211_start,
+ .stop = at76_mac80211_stop,
+ .hw_scan = at76_hw_scan,
+ .set_key = at76_set_key,
+};
+
+/* Allocate network device and initialize private data */
+static struct at76_priv *at76_alloc_new_device(struct usb_device *udev)
+{
+ struct ieee80211_hw *hw;
+ struct at76_priv *priv;
+
+ hw = ieee80211_alloc_hw(sizeof(struct at76_priv), &at76_ops);
+ if (!hw) {
+ printk(KERN_ERR DRIVER_NAME ": could not register"
+ " ieee80211_hw\n");
+ return NULL;
+ }
+
+ priv = hw->priv;
+ priv->hw = hw;
+
+ priv->udev = udev;
+
+ mutex_init(&priv->mtx);
+ INIT_WORK(&priv->work_set_promisc, at76_work_set_promisc);
+ INIT_WORK(&priv->work_submit_rx, at76_work_submit_rx);
+ INIT_WORK(&priv->work_join_bssid, at76_work_join_bssid);
+ INIT_DELAYED_WORK(&priv->dwork_hw_scan, at76_dwork_hw_scan);
+
+ tasklet_init(&priv->rx_tasklet, at76_rx_tasklet, 0);
+
+ priv->pm_mode = AT76_PM_OFF;
+ priv->pm_period = 0;
+
+ /* unit us */
+ priv->hw->channel_change_time = 100000;
+
+ return priv;
+}
+
+static int at76_alloc_urbs(struct at76_priv *priv,
+ struct usb_interface *interface)
+{
+ struct usb_endpoint_descriptor *endpoint, *ep_in, *ep_out;
+ int i;
+ int buffer_size;
+ struct usb_host_interface *iface_desc;
+
+ at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__);
+
+ at76_dbg(DBG_URB, "%s: NumEndpoints %d ", __func__,
+ interface->altsetting[0].desc.bNumEndpoints);
+
+ ep_in = NULL;
+ ep_out = NULL;
+ iface_desc = interface->cur_altsetting;
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ at76_dbg(DBG_URB, "%s: %d. endpoint: addr 0x%x attr 0x%x",
+ __func__, i, endpoint->bEndpointAddress,
+ endpoint->bmAttributes);
+
+ if (!ep_in && usb_endpoint_is_bulk_in(endpoint))
+ ep_in = endpoint;
+
+ if (!ep_out && usb_endpoint_is_bulk_out(endpoint))
+ ep_out = endpoint;
+ }
+
+ if (!ep_in || !ep_out) {
+ dev_printk(KERN_ERR, &interface->dev,
+ "bulk endpoints missing\n");
+ return -ENXIO;
+ }
+
+ priv->rx_pipe = usb_rcvbulkpipe(priv->udev, ep_in->bEndpointAddress);
+ priv->tx_pipe = usb_sndbulkpipe(priv->udev, ep_out->bEndpointAddress);
+
+ priv->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ priv->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!priv->rx_urb || !priv->tx_urb) {
+ dev_printk(KERN_ERR, &interface->dev, "cannot allocate URB\n");
+ return -ENOMEM;
+ }
+
+ buffer_size = sizeof(struct at76_tx_buffer) + MAX_PADDING_SIZE;
+ priv->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (!priv->bulk_out_buffer) {
+ dev_printk(KERN_ERR, &interface->dev,
+ "cannot allocate output buffer\n");
+ return -ENOMEM;
+ }
+
+ at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__);
+
+ return 0;
+}
+
+static struct ieee80211_rate at76_rates[] = {
+ { .bitrate = 10, .hw_value = TX_RATE_1MBIT, },
+ { .bitrate = 20, .hw_value = TX_RATE_2MBIT, },
+ { .bitrate = 55, .hw_value = TX_RATE_5_5MBIT, },
+ { .bitrate = 110, .hw_value = TX_RATE_11MBIT, },
+};
+
+static struct ieee80211_channel at76_channels[] = {
+ { .center_freq = 2412, .hw_value = 1 },
+ { .center_freq = 2417, .hw_value = 2 },
+ { .center_freq = 2422, .hw_value = 3 },
+ { .center_freq = 2427, .hw_value = 4 },
+ { .center_freq = 2432, .hw_value = 5 },
+ { .center_freq = 2437, .hw_value = 6 },
+ { .center_freq = 2442, .hw_value = 7 },
+ { .center_freq = 2447, .hw_value = 8 },
+ { .center_freq = 2452, .hw_value = 9 },
+ { .center_freq = 2457, .hw_value = 10 },
+ { .center_freq = 2462, .hw_value = 11 },
+ { .center_freq = 2467, .hw_value = 12 },
+ { .center_freq = 2472, .hw_value = 13 },
+ { .center_freq = 2484, .hw_value = 14 }
+};
+
+static struct ieee80211_supported_band at76_supported_band = {
+ .channels = at76_channels,
+ .n_channels = ARRAY_SIZE(at76_channels),
+ .bitrates = at76_rates,
+ .n_bitrates = ARRAY_SIZE(at76_rates),
+};
+
+/* Register network device and initialize the hardware */
+static int at76_init_new_device(struct at76_priv *priv,
+ struct usb_interface *interface)
+{
+ struct wiphy *wiphy;
+ size_t len;
+ int ret;
+
+ /* set up the endpoint information */
+ /* check out the endpoints */
+
+ at76_dbg(DBG_DEVSTART, "USB interface: %d endpoints",
+ interface->cur_altsetting->desc.bNumEndpoints);
+
+ ret = at76_alloc_urbs(priv, interface);
+ if (ret < 0)
+ goto exit;
+
+ /* MAC address */
+ ret = at76_get_hw_config(priv);
+ if (ret < 0) {
+ dev_printk(KERN_ERR, &interface->dev,
+ "cannot get MAC address\n");
+ goto exit;
+ }
+
+ priv->domain = at76_get_reg_domain(priv->regulatory_domain);
+
+ priv->channel = DEF_CHANNEL;
+ priv->iw_mode = IW_MODE_INFRA;
+ priv->rts_threshold = DEF_RTS_THRESHOLD;
+ priv->frag_threshold = DEF_FRAG_THRESHOLD;
+ priv->short_retry_limit = DEF_SHORT_RETRY_LIMIT;
+ priv->txrate = TX_RATE_AUTO;
+ priv->preamble_type = PREAMBLE_TYPE_LONG;
+ priv->beacon_period = 100;
+ priv->auth_mode = WLAN_AUTH_OPEN;
+ priv->scan_min_time = DEF_SCAN_MIN_TIME;
+ priv->scan_max_time = DEF_SCAN_MAX_TIME;
+ priv->scan_mode = SCAN_TYPE_ACTIVE;
+ priv->device_unplugged = 0;
+
+ /* mac80211 initialisation */
+ wiphy = priv->hw->wiphy;
+ priv->hw->wiphy->max_scan_ssids = 1;
+ priv->hw->wiphy->max_scan_ie_len = 0;
+ priv->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+ priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &at76_supported_band;
+ priv->hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+ IEEE80211_HW_SIGNAL_UNSPEC;
+ priv->hw->max_signal = 100;
+
+ SET_IEEE80211_DEV(priv->hw, &interface->dev);
+ SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr);
+
+ len = sizeof(wiphy->fw_version);
+ snprintf(wiphy->fw_version, len, "%d.%d.%d-%d",
+ priv->fw_version.major, priv->fw_version.minor,
+ priv->fw_version.patch, priv->fw_version.build);
+
+ wiphy->hw_version = priv->board_type;
+
+ ret = ieee80211_register_hw(priv->hw);
+ if (ret) {
+ printk(KERN_ERR "cannot register mac80211 hw (status %d)!\n",
+ ret);
+ goto exit;
+ }
+
+ priv->mac80211_registered = 1;
+
+ wiphy_info(priv->hw->wiphy, "USB %s, MAC %pM, firmware %d.%d.%d-%d\n",
+ dev_name(&interface->dev), priv->mac_addr,
+ priv->fw_version.major, priv->fw_version.minor,
+ priv->fw_version.patch, priv->fw_version.build);
+ wiphy_info(priv->hw->wiphy, "regulatory domain 0x%02x: %s\n",
+ priv->regulatory_domain, priv->domain->name);
+
+exit:
+ return ret;
+}
+
+static void at76_delete_device(struct at76_priv *priv)
+{
+ at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__);
+
+ /* The device is gone, don't bother turning it off */
+ priv->device_unplugged = 1;
+
+ tasklet_kill(&priv->rx_tasklet);
+
+ if (priv->mac80211_registered)
+ ieee80211_unregister_hw(priv->hw);
+
+ if (priv->tx_urb) {
+ usb_kill_urb(priv->tx_urb);
+ usb_free_urb(priv->tx_urb);
+ }
+ if (priv->rx_urb) {
+ usb_kill_urb(priv->rx_urb);
+ usb_free_urb(priv->rx_urb);
+ }
+
+ at76_dbg(DBG_PROC_ENTRY, "%s: unlinked urbs", __func__);
+
+ kfree(priv->bulk_out_buffer);
+
+ del_timer_sync(&ledtrig_tx_timer);
+
+ kfree_skb(priv->rx_skb);
+
+ usb_put_dev(priv->udev);
+
+ at76_dbg(DBG_PROC_ENTRY, "%s: before freeing priv/ieee80211_hw",
+ __func__);
+ ieee80211_free_hw(priv->hw);
+
+ at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__);
+}
+
+static int at76_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ int ret;
+ struct at76_priv *priv;
+ struct fwentry *fwe;
+ struct usb_device *udev;
+ int op_mode;
+ int need_ext_fw = 0;
+ struct mib_fw_version fwv;
+ int board_type = (int)id->driver_info;
+
+ udev = usb_get_dev(interface_to_usbdev(interface));
+
+ /* Load firmware into kernel memory */
+ fwe = at76_load_firmware(udev, board_type);
+ if (!fwe) {
+ ret = -ENOENT;
+ goto error;
+ }
+
+ op_mode = at76_get_op_mode(udev);
+
+ at76_dbg(DBG_DEVSTART, "opmode %d", op_mode);
+
+ /* we get OPMODE_NONE with 2.4.23, SMC2662W-AR ???
+ we get 204 with 2.4.23, Fiberline FL-WL240u (505A+RFMD2958) ??? */
+
+ if (op_mode == OPMODE_HW_CONFIG_MODE) {
+ dev_printk(KERN_ERR, &interface->dev,
+ "cannot handle a device in HW_CONFIG_MODE\n");
+ ret = -EBUSY;
+ goto error;
+ }
+
+ if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH
+ && op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) {
+ /* download internal firmware part */
+ dev_printk(KERN_DEBUG, &interface->dev,
+ "downloading internal firmware\n");
+ ret = at76_load_internal_fw(udev, fwe);
+ if (ret < 0) {
+ dev_printk(KERN_ERR, &interface->dev,
+ "error %d downloading internal firmware\n",
+ ret);
+ goto error;
+ }
+ usb_put_dev(udev);
+ return ret;
+ }
+
+ /* Internal firmware already inside the device. Get firmware
+ * version to test if external firmware is loaded.
+ * This works only for newer firmware, e.g. the Intersil 0.90.x
+ * says "control timeout on ep0in" and subsequent
+ * at76_get_op_mode() fail too :-( */
+
+ /* if version >= 0.100.x.y or device with built-in flash we can
+ * query the device for the fw version */
+ if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100)
+ || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) {
+ ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv));
+ if (ret < 0 || (fwv.major | fwv.minor) == 0)
+ need_ext_fw = 1;
+ } else
+ /* No way to check firmware version, reload to be sure */
+ need_ext_fw = 1;
+
+ if (need_ext_fw) {
+ dev_printk(KERN_DEBUG, &interface->dev,
+ "downloading external firmware\n");
+
+ ret = at76_load_external_fw(udev, fwe);
+ if (ret)
+ goto error;
+
+ /* Re-check firmware version */
+ ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv));
+ if (ret < 0) {
+ dev_printk(KERN_ERR, &interface->dev,
+ "error %d getting firmware version\n", ret);
+ goto error;
+ }
+ }
+
+ priv = at76_alloc_new_device(udev);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ usb_set_intfdata(interface, priv);
+
+ memcpy(&priv->fw_version, &fwv, sizeof(struct mib_fw_version));
+ priv->board_type = board_type;
+
+ ret = at76_init_new_device(priv, interface);
+ if (ret < 0)
+ at76_delete_device(priv);
+
+ return ret;
+
+error:
+ usb_put_dev(udev);
+ return ret;
+}
+
+static void at76_disconnect(struct usb_interface *interface)
+{
+ struct at76_priv *priv;
+
+ priv = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ /* Disconnect after loading internal firmware */
+ if (!priv)
+ return;
+
+ wiphy_info(priv->hw->wiphy, "disconnecting\n");
+ at76_delete_device(priv);
+ dev_printk(KERN_INFO, &interface->dev, "disconnected\n");
+}
+
+/* Structure for registering this driver with the USB subsystem */
+static struct usb_driver at76_driver = {
+ .name = DRIVER_NAME,
+ .probe = at76_probe,
+ .disconnect = at76_disconnect,
+ .id_table = dev_table,
+};
+
+static int __init at76_mod_init(void)
+{
+ int result;
+
+ printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " loading\n");
+
+ mutex_init(&fw_mutex);
+
+ /* register this driver with the USB subsystem */
+ result = usb_register(&at76_driver);
+ if (result < 0)
+ printk(KERN_ERR DRIVER_NAME
+ ": usb_register failed (status %d)\n", result);
+
+ led_trigger_register_simple("at76_usb-tx", &ledtrig_tx);
+ return result;
+}
+
+static void __exit at76_mod_exit(void)
+{
+ int i;
+
+ printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " unloading\n");
+ usb_deregister(&at76_driver);
+ for (i = 0; i < ARRAY_SIZE(firmwares); i++) {
+ if (firmwares[i].fw)
+ release_firmware(firmwares[i].fw);
+ }
+ led_trigger_unregister_simple(ledtrig_tx);
+}
+
+module_param_named(debug, at76_debug, uint, 0600);
+MODULE_PARM_DESC(debug, "Debugging level");
+
+module_init(at76_mod_init);
+module_exit(at76_mod_exit);
+
+MODULE_AUTHOR("Oliver Kurth <oku@masqmail.cx>");
+MODULE_AUTHOR("Joerg Albert <joerg.albert@gmx.de>");
+MODULE_AUTHOR("Alex <alex@foogod.com>");
+MODULE_AUTHOR("Nick Jones");
+MODULE_AUTHOR("Balint Seeber <n0_5p4m_p13453@hotmail.com>");
+MODULE_AUTHOR("Pavel Roskin <proski@gnu.org>");
+MODULE_AUTHOR("Guido Guenther <agx@sigxcpu.org>");
+MODULE_AUTHOR("Kalle Valo <kalle.valo@iki.fi>");
+MODULE_AUTHOR("Sebastian Smolorz <sesmo@gmx.net>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/at76c50x-usb.h b/drivers/net/wireless/at76c50x-usb.h
new file mode 100644
index 0000000..f14a654
--- /dev/null
+++ b/drivers/net/wireless/at76c50x-usb.h
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) 2002,2003 Oliver Kurth
+ * (c) 2003,2004 Joerg Albert <joerg.albert@gmx.de>
+ * (c) 2007 Guido Guenther <agx@sigxcpu.org>
+ *
+ * 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 driver was based on information from the Sourceforge driver
+ * released and maintained by Atmel:
+ *
+ * http://sourceforge.net/projects/atmelwlandriver/
+ *
+ * Although the code was completely re-written,
+ * it would have been impossible without Atmel's decision to
+ * release an Open Source driver (unfortunately the firmware was
+ * kept binary only). Thanks for that decision to Atmel!
+ */
+
+#ifndef _AT76_USB_H
+#define _AT76_USB_H
+
+/* Board types */
+enum board_type {
+ BOARD_503_ISL3861 = 1,
+ BOARD_503_ISL3863 = 2,
+ BOARD_503 = 3,
+ BOARD_503_ACC = 4,
+ BOARD_505 = 5,
+ BOARD_505_2958 = 6,
+ BOARD_505A = 7,
+ BOARD_505AMX = 8
+};
+
+#define CMD_STATUS_IDLE 0x00
+#define CMD_STATUS_COMPLETE 0x01
+#define CMD_STATUS_UNKNOWN 0x02
+#define CMD_STATUS_INVALID_PARAMETER 0x03
+#define CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04
+#define CMD_STATUS_TIME_OUT 0x07
+#define CMD_STATUS_IN_PROGRESS 0x08
+#define CMD_STATUS_HOST_FAILURE 0xff
+#define CMD_STATUS_SCAN_FAILED 0xf0
+
+/* answers to get op mode */
+#define OPMODE_NONE 0x00
+#define OPMODE_NORMAL_NIC_WITH_FLASH 0x01
+#define OPMODE_HW_CONFIG_MODE 0x02
+#define OPMODE_DFU_MODE_WITH_FLASH 0x03
+#define OPMODE_NORMAL_NIC_WITHOUT_FLASH 0x04
+
+#define CMD_SET_MIB 0x01
+#define CMD_GET_MIB 0x02
+#define CMD_SCAN 0x03
+#define CMD_JOIN 0x04
+#define CMD_START_IBSS 0x05
+#define CMD_RADIO_ON 0x06
+#define CMD_RADIO_OFF 0x07
+#define CMD_STARTUP 0x0B
+
+#define MIB_LOCAL 0x01
+#define MIB_MAC_ADDR 0x02
+#define MIB_MAC 0x03
+#define MIB_MAC_MGMT 0x05
+#define MIB_MAC_WEP 0x06
+#define MIB_PHY 0x07
+#define MIB_FW_VERSION 0x08
+#define MIB_MDOMAIN 0x09
+
+#define ADHOC_MODE 1
+#define INFRASTRUCTURE_MODE 2
+
+/* values for struct mib_local, field preamble_type */
+#define PREAMBLE_TYPE_LONG 0
+#define PREAMBLE_TYPE_SHORT 1
+#define PREAMBLE_TYPE_AUTO 2
+
+/* values for tx_rate */
+#define TX_RATE_1MBIT 0
+#define TX_RATE_2MBIT 1
+#define TX_RATE_5_5MBIT 2
+#define TX_RATE_11MBIT 3
+#define TX_RATE_AUTO 4
+
+/* power management modes */
+#define AT76_PM_OFF 1
+#define AT76_PM_ON 2
+#define AT76_PM_SMART 3
+
+struct hwcfg_r505 {
+ u8 cr39_values[14];
+ u8 reserved1[14];
+ u8 bb_cr[14];
+ u8 pidvid[4];
+ u8 mac_addr[ETH_ALEN];
+ u8 regulatory_domain;
+ u8 reserved2[14];
+ u8 cr15_values[14];
+ u8 reserved3[3];
+} __packed;
+
+struct hwcfg_rfmd {
+ u8 cr20_values[14];
+ u8 cr21_values[14];
+ u8 bb_cr[14];
+ u8 pidvid[4];
+ u8 mac_addr[ETH_ALEN];
+ u8 regulatory_domain;
+ u8 low_power_values[14];
+ u8 normal_power_values[14];
+ u8 reserved1[3];
+} __packed;
+
+struct hwcfg_intersil {
+ u8 mac_addr[ETH_ALEN];
+ u8 cr31_values[14];
+ u8 cr58_values[14];
+ u8 pidvid[4];
+ u8 regulatory_domain;
+ u8 reserved[1];
+} __packed;
+
+union at76_hwcfg {
+ struct hwcfg_intersil i;
+ struct hwcfg_rfmd r3;
+ struct hwcfg_r505 r5;
+};
+
+#define WEP_SMALL_KEY_LEN (40 / 8)
+#define WEP_LARGE_KEY_LEN (104 / 8)
+#define WEP_KEYS (4)
+
+struct at76_card_config {
+ u8 exclude_unencrypted;
+ u8 promiscuous_mode;
+ u8 short_retry_limit;
+ u8 encryption_type;
+ __le16 rts_threshold;
+ __le16 fragmentation_threshold; /* 256..2346 */
+ u8 basic_rate_set[4];
+ u8 auto_rate_fallback; /* 0,1 */
+ u8 channel;
+ u8 privacy_invoked;
+ u8 wep_default_key_id; /* 0..3 */
+ u8 current_ssid[32];
+ u8 wep_default_key_value[4][WEP_LARGE_KEY_LEN];
+ u8 ssid_len;
+ u8 short_preamble;
+ __le16 beacon_period;
+} __packed;
+
+struct at76_command {
+ u8 cmd;
+ u8 reserved;
+ __le16 size;
+ u8 data[0];
+} __packed;
+
+/* Length of Atmel-specific Rx header before 802.11 frame */
+#define AT76_RX_HDRLEN offsetof(struct at76_rx_buffer, packet)
+
+struct at76_rx_buffer {
+ __le16 wlength;
+ u8 rx_rate;
+ u8 newbss;
+ u8 fragmentation;
+ u8 rssi;
+ u8 link_quality;
+ u8 noise_level;
+ __le32 rx_time;
+ u8 packet[IEEE80211_MAX_FRAG_THRESHOLD];
+} __packed;
+
+/* Length of Atmel-specific Tx header before 802.11 frame */
+#define AT76_TX_HDRLEN offsetof(struct at76_tx_buffer, packet)
+
+struct at76_tx_buffer {
+ __le16 wlength;
+ u8 tx_rate;
+ u8 padding;
+ u8 reserved[4];
+ u8 packet[IEEE80211_MAX_FRAG_THRESHOLD];
+} __packed;
+
+/* defines for scan_type below */
+#define SCAN_TYPE_ACTIVE 0
+#define SCAN_TYPE_PASSIVE 1
+
+struct at76_req_scan {
+ u8 bssid[ETH_ALEN];
+ u8 essid[32];
+ u8 scan_type;
+ u8 channel;
+ __le16 probe_delay;
+ __le16 min_channel_time;
+ __le16 max_channel_time;
+ u8 essid_size;
+ u8 international_scan;
+} __packed;
+
+struct at76_req_ibss {
+ u8 bssid[ETH_ALEN];
+ u8 essid[32];
+ u8 bss_type;
+ u8 channel;
+ u8 essid_size;
+ u8 reserved[3];
+} __packed;
+
+struct at76_req_join {
+ u8 bssid[ETH_ALEN];
+ u8 essid[32];
+ u8 bss_type;
+ u8 channel;
+ __le16 timeout;
+ u8 essid_size;
+ u8 reserved;
+} __packed;
+
+struct set_mib_buffer {
+ u8 type;
+ u8 size;
+ u8 index;
+ u8 reserved;
+ union {
+ u8 byte;
+ __le16 word;
+ u8 addr[ETH_ALEN];
+ } data;
+} __packed;
+
+struct mib_local {
+ u16 reserved0;
+ u8 beacon_enable;
+ u8 txautorate_fallback;
+ u8 reserved1;
+ u8 ssid_size;
+ u8 promiscuous_mode;
+ u16 reserved2;
+ u8 preamble_type;
+ u16 reserved3;
+} __packed;
+
+struct mib_mac_addr {
+ u8 mac_addr[ETH_ALEN];
+ u8 res[2]; /* ??? */
+ u8 group_addr[4][ETH_ALEN];
+ u8 group_addr_status[4];
+} __packed;
+
+struct mib_mac {
+ __le32 max_tx_msdu_lifetime;
+ __le32 max_rx_lifetime;
+ __le16 frag_threshold;
+ __le16 rts_threshold;
+ __le16 cwmin;
+ __le16 cwmax;
+ u8 short_retry_time;
+ u8 long_retry_time;
+ u8 scan_type; /* active or passive */
+ u8 scan_channel;
+ __le16 probe_delay; /* delay before ProbeReq in active scan, RO */
+ __le16 min_channel_time;
+ __le16 max_channel_time;
+ __le16 listen_interval;
+ u8 desired_ssid[32];
+ u8 desired_bssid[ETH_ALEN];
+ u8 desired_bsstype; /* ad-hoc or infrastructure */
+ u8 reserved2;
+} __packed;
+
+struct mib_mac_mgmt {
+ __le16 beacon_period;
+ __le16 CFP_max_duration;
+ __le16 medium_occupancy_limit;
+ __le16 station_id; /* assoc id */
+ __le16 ATIM_window;
+ u8 CFP_mode;
+ u8 privacy_option_implemented;
+ u8 DTIM_period;
+ u8 CFP_period;
+ u8 current_bssid[ETH_ALEN];
+ u8 current_essid[32];
+ u8 current_bss_type;
+ u8 power_mgmt_mode;
+ /* rfmd and 505 */
+ u8 ibss_change;
+ u8 res;
+ u8 multi_domain_capability_implemented;
+ u8 multi_domain_capability_enabled;
+ u8 country_string[IEEE80211_COUNTRY_STRING_LEN];
+ u8 reserved[3];
+} __packed;
+
+struct mib_mac_wep {
+ u8 privacy_invoked; /* 0 disable encr., 1 enable encr */
+ u8 wep_default_key_id;
+ u8 wep_key_mapping_len;
+ u8 exclude_unencrypted;
+ __le32 wep_icv_error_count;
+ __le32 wep_excluded_count;
+ u8 wep_default_keyvalue[WEP_KEYS][WEP_LARGE_KEY_LEN];
+ u8 encryption_level; /* 1 for 40bit, 2 for 104bit encryption */
+} __packed;
+
+struct mib_phy {
+ __le32 ed_threshold;
+
+ __le16 slot_time;
+ __le16 sifs_time;
+ __le16 preamble_length;
+ __le16 plcp_header_length;
+ __le16 mpdu_max_length;
+ __le16 cca_mode_supported;
+
+ u8 operation_rate_set[4];
+ u8 channel_id;
+ u8 current_cca_mode;
+ u8 phy_type;
+ u8 current_reg_domain;
+} __packed;
+
+struct mib_fw_version {
+ u8 major;
+ u8 minor;
+ u8 patch;
+ u8 build;
+} __packed;
+
+struct mib_mdomain {
+ u8 tx_powerlevel[14];
+ u8 channel_list[14]; /* 0 for invalid channels */
+} __packed;
+
+struct at76_fw_header {
+ __le32 crc; /* CRC32 of the whole image */
+ __le32 board_type; /* firmware compatibility code */
+ u8 build; /* firmware build number */
+ u8 patch; /* firmware patch level */
+ u8 minor; /* firmware minor version */
+ u8 major; /* firmware major version */
+ __le32 str_offset; /* offset of the copyright string */
+ __le32 int_fw_offset; /* internal firmware image offset */
+ __le32 int_fw_len; /* internal firmware image length */
+ __le32 ext_fw_offset; /* external firmware image offset */
+ __le32 ext_fw_len; /* external firmware image length */
+} __packed;
+
+/* a description of a regulatory domain and the allowed channels */
+struct reg_domain {
+ u16 code;
+ char const *name;
+ u32 channel_map; /* if bit N is set, channel (N+1) is allowed */
+};
+
+/* Data for one loaded firmware file */
+struct fwentry {
+ const char *const fwname;
+ const struct firmware *fw;
+ int extfw_size;
+ int intfw_size;
+ /* pointer to loaded firmware, no need to free */
+ u8 *extfw; /* external firmware, extfw_size bytes long */
+ u8 *intfw; /* internal firmware, intfw_size bytes long */
+ enum board_type board_type; /* board type */
+ struct mib_fw_version fw_version;
+ int loaded; /* Loaded and parsed successfully */
+};
+
+struct at76_priv {
+ struct usb_device *udev; /* USB device pointer */
+
+ struct sk_buff *rx_skb; /* skbuff for receiving data */
+ struct sk_buff *tx_skb; /* skbuff for transmitting data */
+ void *bulk_out_buffer; /* buffer for sending data */
+
+ struct urb *tx_urb; /* URB for sending data */
+ struct urb *rx_urb; /* URB for receiving data */
+
+ unsigned int tx_pipe; /* bulk out pipe */
+ unsigned int rx_pipe; /* bulk in pipe */
+
+ struct mutex mtx; /* locks this structure */
+
+ /* work queues */
+ struct work_struct work_set_promisc;
+ struct work_struct work_submit_rx;
+ struct work_struct work_join_bssid;
+ struct delayed_work dwork_hw_scan;
+
+ struct tasklet_struct rx_tasklet;
+
+ /* the WEP stuff */
+ int wep_enabled; /* 1 if WEP is enabled */
+ int wep_key_id; /* key id to be used */
+ u8 wep_keys[WEP_KEYS][WEP_LARGE_KEY_LEN]; /* WEP keys */
+ u8 wep_keys_len[WEP_KEYS]; /* length of WEP keys */
+
+ int channel;
+ int iw_mode;
+ u8 bssid[ETH_ALEN];
+ u8 essid[IW_ESSID_MAX_SIZE];
+ int essid_size;
+ int radio_on;
+ int promisc;
+
+ int preamble_type; /* 0 - long, 1 - short, 2 - auto */
+ int auth_mode; /* authentication type: 0 open, 1 shared key */
+ int txrate; /* 0,1,2,3 = 1,2,5.5,11 Mbps, 4 is auto */
+ int frag_threshold; /* threshold for fragmentation of tx packets */
+ int rts_threshold; /* threshold for RTS mechanism */
+ int short_retry_limit;
+
+ int scan_min_time; /* scan min channel time */
+ int scan_max_time; /* scan max channel time */
+ int scan_mode; /* SCAN_TYPE_ACTIVE, SCAN_TYPE_PASSIVE */
+ int scan_need_any; /* if set, need to scan for any ESSID */
+
+ u16 assoc_id; /* current association ID, if associated */
+
+ u8 pm_mode; /* power management mode */
+ u32 pm_period; /* power management period in microseconds */
+
+ struct reg_domain const *domain; /* reg domain description */
+
+ /* These fields contain HW config provided by the device (not all of
+ * these fields are used by all board types) */
+ u8 mac_addr[ETH_ALEN];
+ u8 regulatory_domain;
+
+ struct at76_card_config card_config;
+
+ enum board_type board_type;
+ struct mib_fw_version fw_version;
+
+ unsigned int device_unplugged:1;
+ unsigned int netdev_registered:1;
+ struct set_mib_buffer mib_buf; /* global buffer for set_mib calls */
+
+ int beacon_period; /* period of mgmt beacons, Kus */
+
+ struct ieee80211_hw *hw;
+ int mac80211_registered;
+};
+
+#define AT76_SUPPORTED_FILTERS FIF_PROMISC_IN_BSS
+
+#define SCAN_POLL_INTERVAL (HZ / 4)
+
+#define CMD_COMPLETION_TIMEOUT (5 * HZ)
+
+#define DEF_RTS_THRESHOLD 1536
+#define DEF_FRAG_THRESHOLD 1536
+#define DEF_SHORT_RETRY_LIMIT 8
+#define DEF_CHANNEL 10
+#define DEF_SCAN_MIN_TIME 10
+#define DEF_SCAN_MAX_TIME 120
+
+/* the max padding size for tx in bytes (see calc_padding) */
+#define MAX_PADDING_SIZE 53
+
+#endif /* _AT76_USB_H */
diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig
new file mode 100644
index 0000000..0960224
--- /dev/null
+++ b/drivers/net/wireless/ath/Kconfig
@@ -0,0 +1,30 @@
+menuconfig ATH_COMMON
+ tristate "Atheros Wireless Cards"
+ depends on CFG80211 && (!UML || BROKEN)
+ ---help---
+ This will enable the support for the Atheros wireless drivers.
+ ath5k, ath9k, ath9k_htc and ar9170 drivers share some common code, this option
+ enables the common ath.ko module which shares common helpers.
+
+ For more information and documentation on this module you can visit:
+
+ http://wireless.kernel.org/en/users/Drivers/ath
+
+ For information on all Atheros wireless drivers visit:
+
+ http://wireless.kernel.org/en/users/Drivers/Atheros
+
+if ATH_COMMON
+
+config ATH_DEBUG
+ bool "Atheros wireless debugging"
+ ---help---
+ Say Y, if you want to debug atheros wireless drivers.
+ Right now only ath9k makes use of this.
+
+source "drivers/net/wireless/ath/ath5k/Kconfig"
+source "drivers/net/wireless/ath/ath9k/Kconfig"
+source "drivers/net/wireless/ath/carl9170/Kconfig"
+source "drivers/net/wireless/ath/ath6kl/Kconfig"
+
+endif
diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile
new file mode 100644
index 0000000..d121469
--- /dev/null
+++ b/drivers/net/wireless/ath/Makefile
@@ -0,0 +1,13 @@
+obj-$(CONFIG_ATH5K) += ath5k/
+obj-$(CONFIG_ATH9K_HW) += ath9k/
+obj-$(CONFIG_CARL9170) += carl9170/
+obj-$(CONFIG_ATH6KL) += ath6kl/
+
+obj-$(CONFIG_ATH_COMMON) += ath.o
+
+ath-objs := main.o \
+ regd.o \
+ hw.o \
+ key.o
+
+ath-$(CONFIG_ATH_DEBUG) += debug.o
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
new file mode 100644
index 0000000..4596c33
--- /dev/null
+++ b/drivers/net/wireless/ath/ath.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications 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.
+ */
+
+#ifndef ATH_H
+#define ATH_H
+
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+#include <linux/spinlock.h>
+#include <net/mac80211.h>
+
+/*
+ * The key cache is used for h/w cipher state and also for
+ * tracking station state such as the current tx antenna.
+ * We also setup a mapping table between key cache slot indices
+ * and station state to short-circuit node lookups on rx.
+ * Different parts have different size key caches. We handle
+ * up to ATH_KEYMAX entries (could dynamically allocate state).
+ */
+#define ATH_KEYMAX 128 /* max key cache size we handle */
+
+static const u8 ath_bcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+struct ath_ani {
+ bool caldone;
+ unsigned int longcal_timer;
+ unsigned int shortcal_timer;
+ unsigned int resetcal_timer;
+ unsigned int checkani_timer;
+ struct timer_list timer;
+};
+
+struct ath_cycle_counters {
+ u32 cycles;
+ u32 rx_busy;
+ u32 rx_frame;
+ u32 tx_frame;
+};
+
+enum ath_device_state {
+ ATH_HW_UNAVAILABLE,
+ ATH_HW_INITIALIZED,
+};
+
+enum ath_bus_type {
+ ATH_PCI,
+ ATH_AHB,
+ ATH_USB,
+};
+
+struct reg_dmn_pair_mapping {
+ u16 regDmnEnum;
+ u16 reg_5ghz_ctl;
+ u16 reg_2ghz_ctl;
+};
+
+struct ath_regulatory {
+ char alpha2[2];
+ u16 country_code;
+ u16 max_power_level;
+ u16 current_rd;
+ int16_t power_limit;
+ struct reg_dmn_pair_mapping *regpair;
+};
+
+enum ath_crypt_caps {
+ ATH_CRYPT_CAP_CIPHER_AESCCM = BIT(0),
+ ATH_CRYPT_CAP_MIC_COMBINED = BIT(1),
+};
+
+struct ath_keyval {
+ u8 kv_type;
+ u8 kv_pad;
+ u16 kv_len;
+ u8 kv_val[16]; /* TK */
+ u8 kv_mic[8]; /* Michael MIC key */
+ u8 kv_txmic[8]; /* Michael MIC TX key (used only if the hardware
+ * supports both MIC keys in the same key cache entry;
+ * in that case, kv_mic is the RX key) */
+};
+
+enum ath_cipher {
+ ATH_CIPHER_WEP = 0,
+ ATH_CIPHER_AES_OCB = 1,
+ ATH_CIPHER_AES_CCM = 2,
+ ATH_CIPHER_CKIP = 3,
+ ATH_CIPHER_TKIP = 4,
+ ATH_CIPHER_CLR = 5,
+ ATH_CIPHER_MIC = 127
+};
+
+/**
+ * struct ath_ops - Register read/write operations
+ *
+ * @read: Register read
+ * @multi_read: Multiple register read
+ * @write: Register write
+ * @enable_write_buffer: Enable multiple register writes
+ * @write_flush: flush buffered register writes and disable buffering
+ */
+struct ath_ops {
+ unsigned int (*read)(void *, u32 reg_offset);
+ void (*multi_read)(void *, u32 *addr, u32 *val, u16 count);
+ void (*write)(void *, u32 val, u32 reg_offset);
+ void (*enable_write_buffer)(void *);
+ void (*write_flush) (void *);
+ u32 (*rmw)(void *, u32 reg_offset, u32 set, u32 clr);
+};
+
+struct ath_common;
+struct ath_bus_ops;
+
+struct ath_common {
+ void *ah;
+ void *priv;
+ struct ieee80211_hw *hw;
+ int debug_mask;
+ enum ath_device_state state;
+
+ struct ath_ani ani;
+
+ u16 cachelsz;
+ u16 curaid;
+ u8 macaddr[ETH_ALEN];
+ u8 curbssid[ETH_ALEN];
+ u8 bssidmask[ETH_ALEN];
+
+ u32 rx_bufsize;
+
+ u32 keymax;
+ DECLARE_BITMAP(keymap, ATH_KEYMAX);
+ DECLARE_BITMAP(tkip_keymap, ATH_KEYMAX);
+ enum ath_crypt_caps crypt_caps;
+
+ unsigned int clockrate;
+
+ spinlock_t cc_lock;
+ struct ath_cycle_counters cc_ani;
+ struct ath_cycle_counters cc_survey;
+
+ struct ath_regulatory regulatory;
+ const struct ath_ops *ops;
+ const struct ath_bus_ops *bus_ops;
+
+ bool btcoex_enabled;
+ bool disable_ani;
+};
+
+struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
+ u32 len,
+ gfp_t gfp_mask);
+
+void ath_hw_setbssidmask(struct ath_common *common);
+void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key);
+int ath_key_config(struct ath_common *common,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key);
+bool ath_hw_keyreset(struct ath_common *common, u16 entry);
+void ath_hw_cycle_counters_update(struct ath_common *common);
+int32_t ath_hw_get_listen_time(struct ath_common *common);
+
+extern __printf(2, 3) void ath_printk(const char *level, const char *fmt, ...);
+
+#define _ath_printk(level, common, fmt, ...) \
+do { \
+ __always_unused struct ath_common *unused = common; \
+ ath_printk(level, fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define ath_emerg(common, fmt, ...) \
+ _ath_printk(KERN_EMERG, common, fmt, ##__VA_ARGS__)
+#define ath_alert(common, fmt, ...) \
+ _ath_printk(KERN_ALERT, common, fmt, ##__VA_ARGS__)
+#define ath_crit(common, fmt, ...) \
+ _ath_printk(KERN_CRIT, common, fmt, ##__VA_ARGS__)
+#define ath_err(common, fmt, ...) \
+ _ath_printk(KERN_ERR, common, fmt, ##__VA_ARGS__)
+#define ath_warn(common, fmt, ...) \
+ _ath_printk(KERN_WARNING, common, fmt, ##__VA_ARGS__)
+#define ath_notice(common, fmt, ...) \
+ _ath_printk(KERN_NOTICE, common, fmt, ##__VA_ARGS__)
+#define ath_info(common, fmt, ...) \
+ _ath_printk(KERN_INFO, common, fmt, ##__VA_ARGS__)
+
+/**
+ * enum ath_debug_level - atheros wireless debug level
+ *
+ * @ATH_DBG_RESET: reset processing
+ * @ATH_DBG_QUEUE: hardware queue management
+ * @ATH_DBG_EEPROM: eeprom processing
+ * @ATH_DBG_CALIBRATE: periodic calibration
+ * @ATH_DBG_INTERRUPT: interrupt processing
+ * @ATH_DBG_REGULATORY: regulatory processing
+ * @ATH_DBG_ANI: adaptive noise immunitive processing
+ * @ATH_DBG_XMIT: basic xmit operation
+ * @ATH_DBG_BEACON: beacon handling
+ * @ATH_DBG_CONFIG: configuration of the hardware
+ * @ATH_DBG_FATAL: fatal errors, this is the default, DBG_DEFAULT
+ * @ATH_DBG_PS: power save processing
+ * @ATH_DBG_HWTIMER: hardware timer handling
+ * @ATH_DBG_BTCOEX: bluetooth coexistance
+ * @ATH_DBG_BSTUCK: stuck beacons
+ * @ATH_DBG_ANY: enable all debugging
+ *
+ * The debug level is used to control the amount and type of debugging output
+ * we want to see. Each driver has its own method for enabling debugging and
+ * modifying debug level states -- but this is typically done through a
+ * module parameter 'debug' along with a respective 'debug' debugfs file
+ * entry.
+ */
+enum ATH_DEBUG {
+ ATH_DBG_RESET = 0x00000001,
+ ATH_DBG_QUEUE = 0x00000002,
+ ATH_DBG_EEPROM = 0x00000004,
+ ATH_DBG_CALIBRATE = 0x00000008,
+ ATH_DBG_INTERRUPT = 0x00000010,
+ ATH_DBG_REGULATORY = 0x00000020,
+ ATH_DBG_ANI = 0x00000040,
+ ATH_DBG_XMIT = 0x00000080,
+ ATH_DBG_BEACON = 0x00000100,
+ ATH_DBG_CONFIG = 0x00000200,
+ ATH_DBG_FATAL = 0x00000400,
+ ATH_DBG_PS = 0x00000800,
+ ATH_DBG_HWTIMER = 0x00001000,
+ ATH_DBG_BTCOEX = 0x00002000,
+ ATH_DBG_WMI = 0x00004000,
+ ATH_DBG_BSTUCK = 0x00008000,
+ ATH_DBG_MCI = 0x00010000,
+ ATH_DBG_ANY = 0xffffffff
+};
+
+#define ATH_DBG_DEFAULT (ATH_DBG_FATAL)
+
+#ifdef CONFIG_ATH_DEBUG
+
+#define ath_dbg(common, dbg_mask, fmt, ...) \
+do { \
+ if ((common)->debug_mask & dbg_mask) \
+ _ath_printk(KERN_DEBUG, common, fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define ATH_DBG_WARN(foo, arg...) WARN(foo, arg)
+#define ATH_DBG_WARN_ON_ONCE(foo) WARN_ON_ONCE(foo)
+
+#else
+
+static inline __attribute__ ((format (printf, 3, 4)))
+void ath_dbg(struct ath_common *common, enum ATH_DEBUG dbg_mask,
+ const char *fmt, ...)
+{
+}
+#define ATH_DBG_WARN(foo, arg...) do {} while (0)
+#define ATH_DBG_WARN_ON_ONCE(foo) ({ \
+ int __ret_warn_once = !!(foo); \
+ unlikely(__ret_warn_once); \
+})
+
+#endif /* CONFIG_ATH_DEBUG */
+
+/** Returns string describing opmode, or NULL if unknown mode. */
+#ifdef CONFIG_ATH_DEBUG
+const char *ath_opmode_to_string(enum nl80211_iftype opmode);
+#else
+static inline const char *ath_opmode_to_string(enum nl80211_iftype opmode)
+{
+ return "UNKNOWN";
+}
+#endif
+
+#endif /* ATH_H */
diff --git a/drivers/net/wireless/ath/ath5k/Kconfig b/drivers/net/wireless/ath/ath5k/Kconfig
new file mode 100644
index 0000000..e18a9aa
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/Kconfig
@@ -0,0 +1,66 @@
+config ATH5K
+ tristate "Atheros 5xxx wireless cards support"
+ depends on (PCI || ATHEROS_AR231X) && MAC80211
+ select MAC80211_LEDS
+ select LEDS_CLASS
+ select NEW_LEDS
+ select AVERAGE
+ select ATH5K_AHB if (ATHEROS_AR231X && !PCI)
+ select ATH5K_PCI if (!ATHEROS_AR231X && PCI)
+ ---help---
+ This module adds support for wireless adapters based on
+ Atheros 5xxx chipset.
+
+ Currently the following chip versions are supported:
+
+ MAC: AR5211 AR5212
+ PHY: RF5111/2111 RF5112/2112 RF5413/2413
+
+ This driver uses the kernel's mac80211 subsystem.
+
+ If you choose to build a module, it'll be called ath5k. Say M if
+ unsure.
+
+config ATH5K_DEBUG
+ bool "Atheros 5xxx debugging"
+ depends on ATH5K
+ ---help---
+ Atheros 5xxx debugging messages.
+
+ Say Y, if and you will get debug options for ath5k.
+ To use this, you need to mount debugfs:
+
+ mount -t debugfs debug /sys/kernel/debug
+
+ You will get access to files under:
+ /sys/kernel/debug/ath5k/phy0/
+
+ To enable debug, pass the debug level to the debug module
+ parameter. For example:
+
+ modprobe ath5k debug=0x00000400
+
+config ATH5K_TRACER
+ bool "Atheros 5xxx tracer"
+ depends on ATH5K
+ depends on EVENT_TRACING
+ ---help---
+ Say Y here to enable tracepoints for the ath5k driver
+ using the kernel tracing infrastructure. Select this
+ option if you are interested in debugging the driver.
+
+ If unsure, say N.
+
+config ATH5K_AHB
+ bool "Atheros 5xxx AHB bus support"
+ depends on (ATHEROS_AR231X && !PCI)
+ ---help---
+ This adds support for WiSoC type chipsets of the 5xxx Atheros
+ family.
+
+config ATH5K_PCI
+ bool "Atheros 5xxx PCI bus support"
+ depends on (!ATHEROS_AR231X && PCI)
+ ---help---
+ This adds support for PCI type chipsets of the 5xxx Atheros
+ family.
diff --git a/drivers/net/wireless/ath/ath5k/Makefile b/drivers/net/wireless/ath/ath5k/Makefile
new file mode 100644
index 0000000..f60b389
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/Makefile
@@ -0,0 +1,21 @@
+ath5k-y += caps.o
+ath5k-y += initvals.o
+ath5k-y += eeprom.o
+ath5k-y += gpio.o
+ath5k-y += desc.o
+ath5k-y += dma.o
+ath5k-y += qcu.o
+ath5k-y += pcu.o
+ath5k-y += phy.o
+ath5k-y += reset.o
+ath5k-y += attach.o
+ath5k-y += base.o
+ath5k-y += led.o
+ath5k-y += rfkill.o
+ath5k-y += ani.o
+ath5k-y += sysfs.o
+ath5k-y += mac80211-ops.o
+ath5k-$(CONFIG_ATH5K_DEBUG) += debug.o
+ath5k-$(CONFIG_ATH5K_AHB) += ahb.o
+ath5k-$(CONFIG_ATH5K_PCI) += pci.o
+obj-$(CONFIG_ATH5K) += ath5k.o
diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c
new file mode 100644
index 0000000..e5be7e7
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/ahb.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
+ *
+ * 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 <linux/nl80211.h>
+#include <linux/platform_device.h>
+#include <linux/etherdevice.h>
+#include <ar231x_platform.h>
+#include "ath5k.h"
+#include "debug.h"
+#include "base.h"
+#include "reg.h"
+
+/* return bus cachesize in 4B word units */
+static void ath5k_ahb_read_cachesize(struct ath_common *common, int *csz)
+{
+ *csz = L1_CACHE_BYTES >> 2;
+}
+
+static bool
+ath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data)
+{
+ struct ath5k_hw *ah = common->priv;
+ struct platform_device *pdev = to_platform_device(ah->dev);
+ struct ar231x_board_config *bcfg = pdev->dev.platform_data;
+ u16 *eeprom, *eeprom_end;
+
+
+
+ bcfg = pdev->dev.platform_data;
+ eeprom = (u16 *) bcfg->radio;
+ eeprom_end = ((void *) bcfg->config) + BOARD_CONFIG_BUFSZ;
+
+ eeprom += off;
+ if (eeprom > eeprom_end)
+ return false;
+
+ *data = *eeprom;
+ return true;
+}
+
+int ath5k_hw_read_srev(struct ath5k_hw *ah)
+{
+ struct platform_device *pdev = to_platform_device(ah->dev);
+ struct ar231x_board_config *bcfg = pdev->dev.platform_data;
+ ah->ah_mac_srev = bcfg->devid;
+ return 0;
+}
+
+static int ath5k_ahb_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac)
+{
+ struct platform_device *pdev = to_platform_device(ah->dev);
+ struct ar231x_board_config *bcfg = pdev->dev.platform_data;
+ u8 *cfg_mac;
+
+ if (to_platform_device(ah->dev)->id == 0)
+ cfg_mac = bcfg->config->wlan0_mac;
+ else
+ cfg_mac = bcfg->config->wlan1_mac;
+
+ memcpy(mac, cfg_mac, ETH_ALEN);
+ return 0;
+}
+
+static const struct ath_bus_ops ath_ahb_bus_ops = {
+ .ath_bus_type = ATH_AHB,
+ .read_cachesize = ath5k_ahb_read_cachesize,
+ .eeprom_read = ath5k_ahb_eeprom_read,
+ .eeprom_read_mac = ath5k_ahb_eeprom_read_mac,
+};
+
+/*Initialization*/
+static int ath_ahb_probe(struct platform_device *pdev)
+{
+ struct ar231x_board_config *bcfg = pdev->dev.platform_data;
+ struct ath5k_hw *ah;
+ struct ieee80211_hw *hw;
+ struct resource *res;
+ void __iomem *mem;
+ int irq;
+ int ret = 0;
+ u32 reg;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "no platform data specified\n");
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "no memory resource found\n");
+ ret = -ENXIO;
+ goto err_out;
+ }
+
+ mem = ioremap_nocache(res->start, resource_size(res));
+ if (mem == NULL) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "no IRQ resource found\n");
+ ret = -ENXIO;
+ goto err_out;
+ }
+
+ irq = res->start;
+
+ hw = ieee80211_alloc_hw(sizeof(struct ath5k_hw), &ath5k_hw_ops);
+ if (hw == NULL) {
+ dev_err(&pdev->dev, "no memory for ieee80211_hw\n");
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ ah = hw->priv;
+ ah->hw = hw;
+ ah->dev = &pdev->dev;
+ ah->iobase = mem;
+ ah->irq = irq;
+ ah->devid = bcfg->devid;
+
+ if (bcfg->devid >= AR5K_SREV_AR2315_R6) {
+ /* Enable WMAC AHB arbitration */
+ reg = __raw_readl((void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
+ reg |= AR5K_AR2315_AHB_ARB_CTL_WLAN;
+ __raw_writel(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
+
+ /* Enable global WMAC swapping */
+ reg = __raw_readl((void __iomem *) AR5K_AR2315_BYTESWAP);
+ reg |= AR5K_AR2315_BYTESWAP_WMAC;
+ __raw_writel(reg, (void __iomem *) AR5K_AR2315_BYTESWAP);
+ } else {
+ /* Enable WMAC DMA access (assuming 5312 or 231x*/
+ /* TODO: check other platforms */
+ reg = __raw_readl((void __iomem *) AR5K_AR5312_ENABLE);
+ if (to_platform_device(ah->dev)->id == 0)
+ reg |= AR5K_AR5312_ENABLE_WLAN0;
+ else
+ reg |= AR5K_AR5312_ENABLE_WLAN1;
+ __raw_writel(reg, (void __iomem *) AR5K_AR5312_ENABLE);
+
+ /*
+ * On a dual-band AR5312, the multiband radio is only
+ * used as pass-through. Disable 2 GHz support in the
+ * driver for it
+ */
+ if (to_platform_device(ah->dev)->id == 0 &&
+ (bcfg->config->flags & (BD_WLAN0 | BD_WLAN1)) ==
+ (BD_WLAN1 | BD_WLAN0))
+ __set_bit(ATH_STAT_2G_DISABLED, ah->status);
+ }
+
+ ret = ath5k_init_ah(ah, &ath_ahb_bus_ops);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "failed to attach device, err=%d\n", ret);
+ ret = -ENODEV;
+ goto err_free_hw;
+ }
+
+ platform_set_drvdata(pdev, hw);
+
+ return 0;
+
+ err_free_hw:
+ ieee80211_free_hw(hw);
+ platform_set_drvdata(pdev, NULL);
+ err_out:
+ return ret;
+}
+
+static int ath_ahb_remove(struct platform_device *pdev)
+{
+ struct ar231x_board_config *bcfg = pdev->dev.platform_data;
+ struct ieee80211_hw *hw = platform_get_drvdata(pdev);
+ struct ath5k_hw *ah;
+ u32 reg;
+
+ if (!hw)
+ return 0;
+
+ ah = hw->priv;
+
+ if (bcfg->devid >= AR5K_SREV_AR2315_R6) {
+ /* Disable WMAC AHB arbitration */
+ reg = __raw_readl((void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
+ reg &= ~AR5K_AR2315_AHB_ARB_CTL_WLAN;
+ __raw_writel(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
+ } else {
+ /*Stop DMA access */
+ reg = __raw_readl((void __iomem *) AR5K_AR5312_ENABLE);
+ if (to_platform_device(ah->dev)->id == 0)
+ reg &= ~AR5K_AR5312_ENABLE_WLAN0;
+ else
+ reg &= ~AR5K_AR5312_ENABLE_WLAN1;
+ __raw_writel(reg, (void __iomem *) AR5K_AR5312_ENABLE);
+ }
+
+ ath5k_deinit_ah(ah);
+ platform_set_drvdata(pdev, NULL);
+ ieee80211_free_hw(hw);
+
+ return 0;
+}
+
+static struct platform_driver ath_ahb_driver = {
+ .probe = ath_ahb_probe,
+ .remove = ath_ahb_remove,
+ .driver = {
+ .name = "ar231x-wmac",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init
+ath5k_ahb_init(void)
+{
+ return platform_driver_register(&ath_ahb_driver);
+}
+
+static void __exit
+ath5k_ahb_exit(void)
+{
+ platform_driver_unregister(&ath_ahb_driver);
+}
+
+module_init(ath5k_ahb_init);
+module_exit(ath5k_ahb_exit);
diff --git a/drivers/net/wireless/ath/ath5k/ani.c b/drivers/net/wireless/ath/ath5k/ani.c
new file mode 100644
index 0000000..bea90e6
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/ani.c
@@ -0,0 +1,735 @@
+/*
+ * Copyright (C) 2010 Bruno Randolf <br1@einfach.org>
+ *
+ * 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 "ath5k.h"
+#include "reg.h"
+#include "debug.h"
+#include "ani.h"
+
+/**
+ * DOC: Basic ANI Operation
+ *
+ * Adaptive Noise Immunity (ANI) controls five noise immunity parameters
+ * depending on the amount of interference in the environment, increasing
+ * or reducing sensitivity as necessary.
+ *
+ * The parameters are:
+ * - "noise immunity"
+ * - "spur immunity"
+ * - "firstep level"
+ * - "OFDM weak signal detection"
+ * - "CCK weak signal detection"
+ *
+ * Basically we look at the amount of ODFM and CCK timing errors we get and then
+ * raise or lower immunity accordingly by setting one or more of these
+ * parameters.
+ * Newer chipsets have PHY error counters in hardware which will generate a MIB
+ * interrupt when they overflow. Older hardware has too enable PHY error frames
+ * by setting a RX flag and then count every single PHY error. When a specified
+ * threshold of errors has been reached we will raise immunity.
+ * Also we regularly check the amount of errors and lower or raise immunity as
+ * necessary.
+ */
+
+
+/*** ANI parameter control ***/
+
+/**
+ * ath5k_ani_set_noise_immunity_level() - Set noise immunity level
+ *
+ * @level: level between 0 and @ATH5K_ANI_MAX_NOISE_IMM_LVL
+ */
+void
+ath5k_ani_set_noise_immunity_level(struct ath5k_hw *ah, int level)
+{
+ /* TODO:
+ * ANI documents suggest the following five levels to use, but the HAL
+ * and ath9k use only the last two levels, making this
+ * essentially an on/off option. There *may* be a reason for this (???),
+ * so i stick with the HAL version for now...
+ */
+#if 0
+ static const s8 lo[] = { -52, -56, -60, -64, -70 };
+ static const s8 hi[] = { -18, -18, -16, -14, -12 };
+ static const s8 sz[] = { -34, -41, -48, -55, -62 };
+ static const s8 fr[] = { -70, -72, -75, -78, -80 };
+#else
+ static const s8 lo[] = { -64, -70 };
+ static const s8 hi[] = { -14, -12 };
+ static const s8 sz[] = { -55, -62 };
+ static const s8 fr[] = { -78, -80 };
+#endif
+ if (level < 0 || level >= ARRAY_SIZE(sz)) {
+ ATH5K_ERR(ah, "noise immunity level %d out of range",
+ level);
+ return;
+ }
+
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE,
+ AR5K_PHY_DESIRED_SIZE_TOT, sz[level]);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_AGCCOARSE,
+ AR5K_PHY_AGCCOARSE_LO, lo[level]);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_AGCCOARSE,
+ AR5K_PHY_AGCCOARSE_HI, hi[level]);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG,
+ AR5K_PHY_SIG_FIRPWR, fr[level]);
+
+ ah->ani_state.noise_imm_level = level;
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
+}
+
+
+/**
+ * ath5k_ani_set_spur_immunity_level() - Set spur immunity level
+ *
+ * @level: level between 0 and @max_spur_level (the maximum level is dependent
+ * on the chip revision).
+ */
+void
+ath5k_ani_set_spur_immunity_level(struct ath5k_hw *ah, int level)
+{
+ static const int val[] = { 2, 4, 6, 8, 10, 12, 14, 16 };
+
+ if (level < 0 || level >= ARRAY_SIZE(val) ||
+ level > ah->ani_state.max_spur_level) {
+ ATH5K_ERR(ah, "spur immunity level %d out of range",
+ level);
+ return;
+ }
+
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR,
+ AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1, val[level]);
+
+ ah->ani_state.spur_level = level;
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
+}
+
+
+/**
+ * ath5k_ani_set_firstep_level() - Set "firstep" level
+ *
+ * @level: level between 0 and @ATH5K_ANI_MAX_FIRSTEP_LVL
+ */
+void
+ath5k_ani_set_firstep_level(struct ath5k_hw *ah, int level)
+{
+ static const int val[] = { 0, 4, 8 };
+
+ if (level < 0 || level >= ARRAY_SIZE(val)) {
+ ATH5K_ERR(ah, "firstep level %d out of range", level);
+ return;
+ }
+
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG,
+ AR5K_PHY_SIG_FIRSTEP, val[level]);
+
+ ah->ani_state.firstep_level = level;
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
+}
+
+
+/**
+ * ath5k_ani_set_ofdm_weak_signal_detection() - Control OFDM weak signal
+ * detection
+ *
+ * @on: turn on or off
+ */
+void
+ath5k_ani_set_ofdm_weak_signal_detection(struct ath5k_hw *ah, bool on)
+{
+ static const int m1l[] = { 127, 50 };
+ static const int m2l[] = { 127, 40 };
+ static const int m1[] = { 127, 0x4d };
+ static const int m2[] = { 127, 0x40 };
+ static const int m2cnt[] = { 31, 16 };
+ static const int m2lcnt[] = { 63, 48 };
+
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
+ AR5K_PHY_WEAK_OFDM_LOW_THR_M1, m1l[on]);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
+ AR5K_PHY_WEAK_OFDM_LOW_THR_M2, m2l[on]);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR,
+ AR5K_PHY_WEAK_OFDM_HIGH_THR_M1, m1[on]);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR,
+ AR5K_PHY_WEAK_OFDM_HIGH_THR_M2, m2[on]);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR,
+ AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT, m2cnt[on]);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
+ AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT, m2lcnt[on]);
+
+ if (on)
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
+ AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN);
+ else
+ AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
+ AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN);
+
+ ah->ani_state.ofdm_weak_sig = on;
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s",
+ on ? "on" : "off");
+}
+
+
+/**
+ * ath5k_ani_set_cck_weak_signal_detection() - control CCK weak signal detection
+ *
+ * @on: turn on or off
+ */
+void
+ath5k_ani_set_cck_weak_signal_detection(struct ath5k_hw *ah, bool on)
+{
+ static const int val[] = { 8, 6 };
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_CCK_CROSSCORR,
+ AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR, val[on]);
+ ah->ani_state.cck_weak_sig = on;
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s",
+ on ? "on" : "off");
+}
+
+
+/*** ANI algorithm ***/
+
+/**
+ * ath5k_ani_raise_immunity() - Increase noise immunity
+ *
+ * @ofdm_trigger: If this is true we are called because of too many OFDM errors,
+ * the algorithm will tune more parameters then.
+ *
+ * Try to raise noise immunity (=decrease sensitivity) in several steps
+ * depending on the average RSSI of the beacons we received.
+ */
+static void
+ath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as,
+ bool ofdm_trigger)
+{
+ int rssi = ewma_read(&ah->ah_beacon_rssi_avg);
+
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "raise immunity (%s)",
+ ofdm_trigger ? "ODFM" : "CCK");
+
+ /* first: raise noise immunity */
+ if (as->noise_imm_level < ATH5K_ANI_MAX_NOISE_IMM_LVL) {
+ ath5k_ani_set_noise_immunity_level(ah, as->noise_imm_level + 1);
+ return;
+ }
+
+ /* only OFDM: raise spur immunity level */
+ if (ofdm_trigger &&
+ as->spur_level < ah->ani_state.max_spur_level) {
+ ath5k_ani_set_spur_immunity_level(ah, as->spur_level + 1);
+ return;
+ }
+
+ /* AP mode */
+ if (ah->opmode == NL80211_IFTYPE_AP) {
+ if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL)
+ ath5k_ani_set_firstep_level(ah, as->firstep_level + 1);
+ return;
+ }
+
+ /* STA and IBSS mode */
+
+ /* TODO: for IBSS mode it would be better to keep a beacon RSSI average
+ * per each neighbour node and use the minimum of these, to make sure we
+ * don't shut out a remote node by raising immunity too high. */
+
+ if (rssi > ATH5K_ANI_RSSI_THR_HIGH) {
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
+ "beacon RSSI high");
+ /* only OFDM: beacon RSSI is high, we can disable ODFM weak
+ * signal detection */
+ if (ofdm_trigger && as->ofdm_weak_sig == true) {
+ ath5k_ani_set_ofdm_weak_signal_detection(ah, false);
+ ath5k_ani_set_spur_immunity_level(ah, 0);
+ return;
+ }
+ /* as a last resort or CCK: raise firstep level */
+ if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) {
+ ath5k_ani_set_firstep_level(ah, as->firstep_level + 1);
+ return;
+ }
+ } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) {
+ /* beacon RSSI in mid range, we need OFDM weak signal detect,
+ * but can raise firstep level */
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
+ "beacon RSSI mid");
+ if (ofdm_trigger && as->ofdm_weak_sig == false)
+ ath5k_ani_set_ofdm_weak_signal_detection(ah, true);
+ if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL)
+ ath5k_ani_set_firstep_level(ah, as->firstep_level + 1);
+ return;
+ } else if (ah->ah_current_channel->band == IEEE80211_BAND_2GHZ) {
+ /* beacon RSSI is low. in B/G mode turn of OFDM weak signal
+ * detect and zero firstep level to maximize CCK sensitivity */
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
+ "beacon RSSI low, 2GHz");
+ if (ofdm_trigger && as->ofdm_weak_sig == true)
+ ath5k_ani_set_ofdm_weak_signal_detection(ah, false);
+ if (as->firstep_level > 0)
+ ath5k_ani_set_firstep_level(ah, 0);
+ return;
+ }
+
+ /* TODO: why not?:
+ if (as->cck_weak_sig == true) {
+ ath5k_ani_set_cck_weak_signal_detection(ah, false);
+ }
+ */
+}
+
+
+/**
+ * ath5k_ani_lower_immunity() - Decrease noise immunity
+ *
+ * Try to lower noise immunity (=increase sensitivity) in several steps
+ * depending on the average RSSI of the beacons we received.
+ */
+static void
+ath5k_ani_lower_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as)
+{
+ int rssi = ewma_read(&ah->ah_beacon_rssi_avg);
+
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "lower immunity");
+
+ if (ah->opmode == NL80211_IFTYPE_AP) {
+ /* AP mode */
+ if (as->firstep_level > 0) {
+ ath5k_ani_set_firstep_level(ah, as->firstep_level - 1);
+ return;
+ }
+ } else {
+ /* STA and IBSS mode (see TODO above) */
+ if (rssi > ATH5K_ANI_RSSI_THR_HIGH) {
+ /* beacon signal is high, leave OFDM weak signal
+ * detection off or it may oscillate
+ * TODO: who said it's off??? */
+ } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) {
+ /* beacon RSSI is mid-range: turn on ODFM weak signal
+ * detection and next, lower firstep level */
+ if (as->ofdm_weak_sig == false) {
+ ath5k_ani_set_ofdm_weak_signal_detection(ah,
+ true);
+ return;
+ }
+ if (as->firstep_level > 0) {
+ ath5k_ani_set_firstep_level(ah,
+ as->firstep_level - 1);
+ return;
+ }
+ } else {
+ /* beacon signal is low: only reduce firstep level */
+ if (as->firstep_level > 0) {
+ ath5k_ani_set_firstep_level(ah,
+ as->firstep_level - 1);
+ return;
+ }
+ }
+ }
+
+ /* all modes */
+ if (as->spur_level > 0) {
+ ath5k_ani_set_spur_immunity_level(ah, as->spur_level - 1);
+ return;
+ }
+
+ /* finally, reduce noise immunity */
+ if (as->noise_imm_level > 0) {
+ ath5k_ani_set_noise_immunity_level(ah, as->noise_imm_level - 1);
+ return;
+ }
+}
+
+
+/**
+ * ath5k_hw_ani_get_listen_time() - Update counters and return listening time
+ *
+ * Return an approximation of the time spent "listening" in milliseconds (ms)
+ * since the last call of this function.
+ * Save a snapshot of the counter values for debugging/statistics.
+ */
+static int
+ath5k_hw_ani_get_listen_time(struct ath5k_hw *ah, struct ath5k_ani_state *as)
+{
+ struct ath_common *common = ath5k_hw_common(ah);
+ int listen;
+
+ spin_lock_bh(&common->cc_lock);
+
+ ath_hw_cycle_counters_update(common);
+ memcpy(&as->last_cc, &common->cc_ani, sizeof(as->last_cc));
+
+ /* clears common->cc_ani */
+ listen = ath_hw_get_listen_time(common);
+
+ spin_unlock_bh(&common->cc_lock);
+
+ return listen;
+}
+
+
+/**
+ * ath5k_ani_save_and_clear_phy_errors() - Clear and save PHY error counters
+ *
+ * Clear the PHY error counters as soon as possible, since this might be called
+ * from a MIB interrupt and we want to make sure we don't get interrupted again.
+ * Add the count of CCK and OFDM errors to our internal state, so it can be used
+ * by the algorithm later.
+ *
+ * Will be called from interrupt and tasklet context.
+ * Returns 0 if both counters are zero.
+ */
+static int
+ath5k_ani_save_and_clear_phy_errors(struct ath5k_hw *ah,
+ struct ath5k_ani_state *as)
+{
+ unsigned int ofdm_err, cck_err;
+
+ if (!ah->ah_capabilities.cap_has_phyerr_counters)
+ return 0;
+
+ ofdm_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1);
+ cck_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2);
+
+ /* reset counters first, we might be in a hurry (interrupt) */
+ ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH,
+ AR5K_PHYERR_CNT1);
+ ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH,
+ AR5K_PHYERR_CNT2);
+
+ ofdm_err = ATH5K_ANI_OFDM_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - ofdm_err);
+ cck_err = ATH5K_ANI_CCK_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - cck_err);
+
+ /* sometimes both can be zero, especially when there is a superfluous
+ * second interrupt. detect that here and return an error. */
+ if (ofdm_err <= 0 && cck_err <= 0)
+ return 0;
+
+ /* avoid negative values should one of the registers overflow */
+ if (ofdm_err > 0) {
+ as->ofdm_errors += ofdm_err;
+ as->sum_ofdm_errors += ofdm_err;
+ }
+ if (cck_err > 0) {
+ as->cck_errors += cck_err;
+ as->sum_cck_errors += cck_err;
+ }
+ return 1;
+}
+
+
+/**
+ * ath5k_ani_period_restart() - Restart ANI period
+ *
+ * Just reset counters, so they are clear for the next "ani period".
+ */
+static void
+ath5k_ani_period_restart(struct ath5k_hw *ah, struct ath5k_ani_state *as)
+{
+ /* keep last values for debugging */
+ as->last_ofdm_errors = as->ofdm_errors;
+ as->last_cck_errors = as->cck_errors;
+ as->last_listen = as->listen_time;
+
+ as->ofdm_errors = 0;
+ as->cck_errors = 0;
+ as->listen_time = 0;
+}
+
+
+/**
+ * ath5k_ani_calibration() - The main ANI calibration function
+ *
+ * We count OFDM and CCK errors relative to the time where we did not send or
+ * receive ("listen" time) and raise or lower immunity accordingly.
+ * This is called regularly (every second) from the calibration timer, but also
+ * when an error threshold has been reached.
+ *
+ * In order to synchronize access from different contexts, this should be
+ * called only indirectly by scheduling the ANI tasklet!
+ */
+void
+ath5k_ani_calibration(struct ath5k_hw *ah)
+{
+ struct ath5k_ani_state *as = &ah->ani_state;
+ int listen, ofdm_high, ofdm_low, cck_high, cck_low;
+
+ /* get listen time since last call and add it to the counter because we
+ * might not have restarted the "ani period" last time.
+ * always do this to calculate the busy time also in manual mode */
+ listen = ath5k_hw_ani_get_listen_time(ah, as);
+ as->listen_time += listen;
+
+ if (as->ani_mode != ATH5K_ANI_MODE_AUTO)
+ return;
+
+ ath5k_ani_save_and_clear_phy_errors(ah, as);
+
+ ofdm_high = as->listen_time * ATH5K_ANI_OFDM_TRIG_HIGH / 1000;
+ cck_high = as->listen_time * ATH5K_ANI_CCK_TRIG_HIGH / 1000;
+ ofdm_low = as->listen_time * ATH5K_ANI_OFDM_TRIG_LOW / 1000;
+ cck_low = as->listen_time * ATH5K_ANI_CCK_TRIG_LOW / 1000;
+
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
+ "listen %d (now %d)", as->listen_time, listen);
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
+ "check high ofdm %d/%d cck %d/%d",
+ as->ofdm_errors, ofdm_high, as->cck_errors, cck_high);
+
+ if (as->ofdm_errors > ofdm_high || as->cck_errors > cck_high) {
+ /* too many PHY errors - we have to raise immunity */
+ bool ofdm_flag = as->ofdm_errors > ofdm_high ? true : false;
+ ath5k_ani_raise_immunity(ah, as, ofdm_flag);
+ ath5k_ani_period_restart(ah, as);
+
+ } else if (as->listen_time > 5 * ATH5K_ANI_LISTEN_PERIOD) {
+ /* If more than 5 (TODO: why 5?) periods have passed and we got
+ * relatively little errors we can try to lower immunity */
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
+ "check low ofdm %d/%d cck %d/%d",
+ as->ofdm_errors, ofdm_low, as->cck_errors, cck_low);
+
+ if (as->ofdm_errors <= ofdm_low && as->cck_errors <= cck_low)
+ ath5k_ani_lower_immunity(ah, as);
+
+ ath5k_ani_period_restart(ah, as);
+ }
+}
+
+
+/*** INTERRUPT HANDLER ***/
+
+/**
+ * ath5k_ani_mib_intr() - Interrupt handler for ANI MIB counters
+ *
+ * Just read & reset the registers quickly, so they don't generate more
+ * interrupts, save the counters and schedule the tasklet to decide whether
+ * to raise immunity or not.
+ *
+ * We just need to handle PHY error counters, ath5k_hw_update_mib_counters()
+ * should take care of all "normal" MIB interrupts.
+ */
+void
+ath5k_ani_mib_intr(struct ath5k_hw *ah)
+{
+ struct ath5k_ani_state *as = &ah->ani_state;
+
+ /* nothing to do here if HW does not have PHY error counters - they
+ * can't be the reason for the MIB interrupt then */
+ if (!ah->ah_capabilities.cap_has_phyerr_counters)
+ return;
+
+ /* not in use but clear anyways */
+ ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT);
+ ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT);
+
+ if (ah->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO)
+ return;
+
+ /* If one of the errors triggered, we can get a superfluous second
+ * interrupt, even though we have already reset the register. The
+ * function detects that so we can return early. */
+ if (ath5k_ani_save_and_clear_phy_errors(ah, as) == 0)
+ return;
+
+ if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH ||
+ as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH)
+ tasklet_schedule(&ah->ani_tasklet);
+}
+
+
+/**
+ * ath5k_ani_phy_error_report() - Used by older HW to report PHY errors
+ *
+ * This is used by hardware without PHY error counters to report PHY errors
+ * on a frame-by-frame basis, instead of the interrupt.
+ */
+void
+ath5k_ani_phy_error_report(struct ath5k_hw *ah,
+ enum ath5k_phy_error_code phyerr)
+{
+ struct ath5k_ani_state *as = &ah->ani_state;
+
+ if (phyerr == AR5K_RX_PHY_ERROR_OFDM_TIMING) {
+ as->ofdm_errors++;
+ if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH)
+ tasklet_schedule(&ah->ani_tasklet);
+ } else if (phyerr == AR5K_RX_PHY_ERROR_CCK_TIMING) {
+ as->cck_errors++;
+ if (as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH)
+ tasklet_schedule(&ah->ani_tasklet);
+ }
+}
+
+
+/*** INIT ***/
+
+/**
+ * ath5k_enable_phy_err_counters() - Enable PHY error counters
+ *
+ * Enable PHY error counters for OFDM and CCK timing errors.
+ */
+static void
+ath5k_enable_phy_err_counters(struct ath5k_hw *ah)
+{
+ ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH,
+ AR5K_PHYERR_CNT1);
+ ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH,
+ AR5K_PHYERR_CNT2);
+ ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_OFDM, AR5K_PHYERR_CNT1_MASK);
+ ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_CCK, AR5K_PHYERR_CNT2_MASK);
+
+ /* not in use */
+ ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT);
+ ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT);
+}
+
+
+/**
+ * ath5k_disable_phy_err_counters() - Disable PHY error counters
+ *
+ * Disable PHY error counters for OFDM and CCK timing errors.
+ */
+static void
+ath5k_disable_phy_err_counters(struct ath5k_hw *ah)
+{
+ ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1);
+ ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2);
+ ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1_MASK);
+ ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2_MASK);
+
+ /* not in use */
+ ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT);
+ ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT);
+}
+
+
+/**
+ * ath5k_ani_init() - Initialize ANI
+ * @mode: Which mode to use (auto, manual high, manual low, off)
+ *
+ * Initialize ANI according to mode.
+ */
+void
+ath5k_ani_init(struct ath5k_hw *ah, enum ath5k_ani_mode mode)
+{
+ /* ANI is only possible on 5212 and newer */
+ if (ah->ah_version < AR5K_AR5212)
+ return;
+
+ if (mode < ATH5K_ANI_MODE_OFF || mode > ATH5K_ANI_MODE_AUTO) {
+ ATH5K_ERR(ah, "ANI mode %d out of range", mode);
+ return;
+ }
+
+ /* clear old state information */
+ memset(&ah->ani_state, 0, sizeof(ah->ani_state));
+
+ /* older hardware has more spur levels than newer */
+ if (ah->ah_mac_srev < AR5K_SREV_AR2414)
+ ah->ani_state.max_spur_level = 7;
+ else
+ ah->ani_state.max_spur_level = 2;
+
+ /* initial values for our ani parameters */
+ if (mode == ATH5K_ANI_MODE_OFF) {
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI off\n");
+ } else if (mode == ATH5K_ANI_MODE_MANUAL_LOW) {
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
+ "ANI manual low -> high sensitivity\n");
+ ath5k_ani_set_noise_immunity_level(ah, 0);
+ ath5k_ani_set_spur_immunity_level(ah, 0);
+ ath5k_ani_set_firstep_level(ah, 0);
+ ath5k_ani_set_ofdm_weak_signal_detection(ah, true);
+ ath5k_ani_set_cck_weak_signal_detection(ah, true);
+ } else if (mode == ATH5K_ANI_MODE_MANUAL_HIGH) {
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
+ "ANI manual high -> low sensitivity\n");
+ ath5k_ani_set_noise_immunity_level(ah,
+ ATH5K_ANI_MAX_NOISE_IMM_LVL);
+ ath5k_ani_set_spur_immunity_level(ah,
+ ah->ani_state.max_spur_level);
+ ath5k_ani_set_firstep_level(ah, ATH5K_ANI_MAX_FIRSTEP_LVL);
+ ath5k_ani_set_ofdm_weak_signal_detection(ah, false);
+ ath5k_ani_set_cck_weak_signal_detection(ah, false);
+ } else if (mode == ATH5K_ANI_MODE_AUTO) {
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI auto\n");
+ ath5k_ani_set_noise_immunity_level(ah, 0);
+ ath5k_ani_set_spur_immunity_level(ah, 0);
+ ath5k_ani_set_firstep_level(ah, 0);
+ ath5k_ani_set_ofdm_weak_signal_detection(ah, true);
+ ath5k_ani_set_cck_weak_signal_detection(ah, false);
+ }
+
+ /* newer hardware has PHY error counter registers which we can use to
+ * get OFDM and CCK error counts. older hardware has to set rxfilter and
+ * report every single PHY error by calling ath5k_ani_phy_error_report()
+ */
+ if (mode == ATH5K_ANI_MODE_AUTO) {
+ if (ah->ah_capabilities.cap_has_phyerr_counters)
+ ath5k_enable_phy_err_counters(ah);
+ else
+ ath5k_hw_set_rx_filter(ah, ath5k_hw_get_rx_filter(ah) |
+ AR5K_RX_FILTER_PHYERR);
+ } else {
+ if (ah->ah_capabilities.cap_has_phyerr_counters)
+ ath5k_disable_phy_err_counters(ah);
+ else
+ ath5k_hw_set_rx_filter(ah, ath5k_hw_get_rx_filter(ah) &
+ ~AR5K_RX_FILTER_PHYERR);
+ }
+
+ ah->ani_state.ani_mode = mode;
+}
+
+
+/*** DEBUG ***/
+
+#ifdef CONFIG_ATH5K_DEBUG
+
+void
+ath5k_ani_print_counters(struct ath5k_hw *ah)
+{
+ /* clears too */
+ printk(KERN_NOTICE "ACK fail\t%d\n",
+ ath5k_hw_reg_read(ah, AR5K_ACK_FAIL));
+ printk(KERN_NOTICE "RTS fail\t%d\n",
+ ath5k_hw_reg_read(ah, AR5K_RTS_FAIL));
+ printk(KERN_NOTICE "RTS success\t%d\n",
+ ath5k_hw_reg_read(ah, AR5K_RTS_OK));
+ printk(KERN_NOTICE "FCS error\t%d\n",
+ ath5k_hw_reg_read(ah, AR5K_FCS_FAIL));
+
+ /* no clear */
+ printk(KERN_NOTICE "tx\t%d\n",
+ ath5k_hw_reg_read(ah, AR5K_PROFCNT_TX));
+ printk(KERN_NOTICE "rx\t%d\n",
+ ath5k_hw_reg_read(ah, AR5K_PROFCNT_RX));
+ printk(KERN_NOTICE "busy\t%d\n",
+ ath5k_hw_reg_read(ah, AR5K_PROFCNT_RXCLR));
+ printk(KERN_NOTICE "cycles\t%d\n",
+ ath5k_hw_reg_read(ah, AR5K_PROFCNT_CYCLE));
+
+ printk(KERN_NOTICE "AR5K_PHYERR_CNT1\t%d\n",
+ ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1));
+ printk(KERN_NOTICE "AR5K_PHYERR_CNT2\t%d\n",
+ ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2));
+ printk(KERN_NOTICE "AR5K_OFDM_FIL_CNT\t%d\n",
+ ath5k_hw_reg_read(ah, AR5K_OFDM_FIL_CNT));
+ printk(KERN_NOTICE "AR5K_CCK_FIL_CNT\t%d\n",
+ ath5k_hw_reg_read(ah, AR5K_CCK_FIL_CNT));
+}
+
+#endif
diff --git a/drivers/net/wireless/ath/ath5k/ani.h b/drivers/net/wireless/ath/ath5k/ani.h
new file mode 100644
index 0000000..7358b6c
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/ani.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 Bruno Randolf <br1@einfach.org>
+ *
+ * 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.
+ */
+#ifndef ANI_H
+#define ANI_H
+
+#include "../ath.h"
+
+enum ath5k_phy_error_code;
+
+/* these thresholds are relative to the ATH5K_ANI_LISTEN_PERIOD */
+#define ATH5K_ANI_LISTEN_PERIOD 100
+#define ATH5K_ANI_OFDM_TRIG_HIGH 500
+#define ATH5K_ANI_OFDM_TRIG_LOW 200
+#define ATH5K_ANI_CCK_TRIG_HIGH 200
+#define ATH5K_ANI_CCK_TRIG_LOW 100
+
+/* average beacon RSSI thresholds */
+#define ATH5K_ANI_RSSI_THR_HIGH 40
+#define ATH5K_ANI_RSSI_THR_LOW 7
+
+/* maximum available levels */
+#define ATH5K_ANI_MAX_FIRSTEP_LVL 2
+#define ATH5K_ANI_MAX_NOISE_IMM_LVL 1
+
+
+/**
+ * enum ath5k_ani_mode - mode for ANI / noise sensitivity
+ *
+ * @ATH5K_ANI_MODE_OFF: Turn ANI off. This can be useful to just stop the ANI
+ * algorithm after it has been on auto mode.
+ * ATH5K_ANI_MODE_MANUAL_LOW: Manually set all immunity parameters to low,
+ * maximizing sensitivity. ANI will not run.
+ * ATH5K_ANI_MODE_MANUAL_HIGH: Manually set all immunity parameters to high,
+ * minimizing sensitivity. ANI will not run.
+ * ATH5K_ANI_MODE_AUTO: Automatically control immunity parameters based on the
+ * amount of OFDM and CCK frame errors (default).
+ */
+enum ath5k_ani_mode {
+ ATH5K_ANI_MODE_OFF = 0,
+ ATH5K_ANI_MODE_MANUAL_LOW = 1,
+ ATH5K_ANI_MODE_MANUAL_HIGH = 2,
+ ATH5K_ANI_MODE_AUTO = 3
+};
+
+
+/**
+ * struct ath5k_ani_state - ANI state and associated counters
+ *
+ * @max_spur_level: the maximum spur level is chip dependent
+ */
+struct ath5k_ani_state {
+ enum ath5k_ani_mode ani_mode;
+
+ /* state */
+ int noise_imm_level;
+ int spur_level;
+ int firstep_level;
+ bool ofdm_weak_sig;
+ bool cck_weak_sig;
+
+ int max_spur_level;
+
+ /* used by the algorithm */
+ unsigned int listen_time;
+ unsigned int ofdm_errors;
+ unsigned int cck_errors;
+
+ /* debug/statistics only: numbers from last ANI calibration */
+ struct ath_cycle_counters last_cc;
+ unsigned int last_listen;
+ unsigned int last_ofdm_errors;
+ unsigned int last_cck_errors;
+ unsigned int sum_ofdm_errors;
+ unsigned int sum_cck_errors;
+};
+
+void ath5k_ani_init(struct ath5k_hw *ah, enum ath5k_ani_mode mode);
+void ath5k_ani_mib_intr(struct ath5k_hw *ah);
+void ath5k_ani_calibration(struct ath5k_hw *ah);
+void ath5k_ani_phy_error_report(struct ath5k_hw *ah,
+ enum ath5k_phy_error_code phyerr);
+
+/* for manual control */
+void ath5k_ani_set_noise_immunity_level(struct ath5k_hw *ah, int level);
+void ath5k_ani_set_spur_immunity_level(struct ath5k_hw *ah, int level);
+void ath5k_ani_set_firstep_level(struct ath5k_hw *ah, int level);
+void ath5k_ani_set_ofdm_weak_signal_detection(struct ath5k_hw *ah, bool on);
+void ath5k_ani_set_cck_weak_signal_detection(struct ath5k_hw *ah, bool on);
+
+void ath5k_ani_print_counters(struct ath5k_hw *ah);
+
+#endif /* ANI_H */
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
new file mode 100644
index 0000000..fecbcd9
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -0,0 +1,1504 @@
+/*
+ * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2007 Nick Kossifidis <mickflemm@gmail.com>
+ *
+ * Permission to use, copy, modify, and 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.
+ */
+
+#ifndef _ATH5K_H
+#define _ATH5K_H
+
+/* TODO: Clean up channel debugging (doesn't work anyway) and start
+ * working on reg. control code using all available eeprom information
+ * (rev. engineering needed) */
+#define CHAN_DEBUG 0
+
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/average.h>
+#include <linux/leds.h>
+#include <net/mac80211.h>
+
+/* RX/TX descriptor hw structs
+ * TODO: Driver part should only see sw structs */
+#include "desc.h"
+
+/* EEPROM structs/offsets
+ * TODO: Make a more generic struct (eg. add more stuff to ath5k_capabilities)
+ * and clean up common bits, then introduce set/get functions in eeprom.c */
+#include "eeprom.h"
+#include "debug.h"
+#include "../ath.h"
+#include "ani.h"
+
+/* PCI IDs */
+#define PCI_DEVICE_ID_ATHEROS_AR5210 0x0007 /* AR5210 */
+#define PCI_DEVICE_ID_ATHEROS_AR5311 0x0011 /* AR5311 */
+#define PCI_DEVICE_ID_ATHEROS_AR5211 0x0012 /* AR5211 */
+#define PCI_DEVICE_ID_ATHEROS_AR5212 0x0013 /* AR5212 */
+#define PCI_DEVICE_ID_3COM_3CRDAG675 0x0013 /* 3CRDAG675 (Atheros AR5212) */
+#define PCI_DEVICE_ID_3COM_2_3CRPAG175 0x0013 /* 3CRPAG175 (Atheros AR5212) */
+#define PCI_DEVICE_ID_ATHEROS_AR5210_AP 0x0207 /* AR5210 (Early) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_IBM 0x1014 /* AR5212 (IBM MiniPCI) */
+#define PCI_DEVICE_ID_ATHEROS_AR5210_DEFAULT 0x1107 /* AR5210 (no eeprom) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_DEFAULT 0x1113 /* AR5212 (no eeprom) */
+#define PCI_DEVICE_ID_ATHEROS_AR5211_DEFAULT 0x1112 /* AR5211 (no eeprom) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_FPGA 0xf013 /* AR5212 (emulation board) */
+#define PCI_DEVICE_ID_ATHEROS_AR5211_LEGACY 0xff12 /* AR5211 (emulation board) */
+#define PCI_DEVICE_ID_ATHEROS_AR5211_FPGA11B 0xf11b /* AR5211 (emulation board) */
+#define PCI_DEVICE_ID_ATHEROS_AR5312_REV2 0x0052 /* AR5312 WMAC (AP31) */
+#define PCI_DEVICE_ID_ATHEROS_AR5312_REV7 0x0057 /* AR5312 WMAC (AP30-040) */
+#define PCI_DEVICE_ID_ATHEROS_AR5312_REV8 0x0058 /* AR5312 WMAC (AP43-030) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0014 0x0014 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0015 0x0015 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0016 0x0016 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0017 0x0017 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0018 0x0018 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0019 0x0019 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR2413 0x001a /* AR2413 (Griffin-lite) */
+#define PCI_DEVICE_ID_ATHEROS_AR5413 0x001b /* AR5413 (Eagle) */
+#define PCI_DEVICE_ID_ATHEROS_AR5424 0x001c /* AR5424 (Condor PCI-E) */
+#define PCI_DEVICE_ID_ATHEROS_AR5416 0x0023 /* AR5416 */
+#define PCI_DEVICE_ID_ATHEROS_AR5418 0x0024 /* AR5418 */
+
+/****************************\
+ GENERIC DRIVER DEFINITIONS
+\****************************/
+
+#define ATH5K_PRINTF(fmt, ...) \
+ printk(KERN_WARNING "%s: " fmt, __func__, ##__VA_ARGS__)
+
+#define ATH5K_PRINTK(_sc, _level, _fmt, ...) \
+ printk(_level "ath5k %s: " _fmt, \
+ ((_sc) && (_sc)->hw) ? wiphy_name((_sc)->hw->wiphy) : "", \
+ ##__VA_ARGS__)
+
+#define ATH5K_PRINTK_LIMIT(_sc, _level, _fmt, ...) do { \
+ if (net_ratelimit()) \
+ ATH5K_PRINTK(_sc, _level, _fmt, ##__VA_ARGS__); \
+ } while (0)
+
+#define ATH5K_INFO(_sc, _fmt, ...) \
+ ATH5K_PRINTK(_sc, KERN_INFO, _fmt, ##__VA_ARGS__)
+
+#define ATH5K_WARN(_sc, _fmt, ...) \
+ ATH5K_PRINTK_LIMIT(_sc, KERN_WARNING, _fmt, ##__VA_ARGS__)
+
+#define ATH5K_ERR(_sc, _fmt, ...) \
+ ATH5K_PRINTK_LIMIT(_sc, KERN_ERR, _fmt, ##__VA_ARGS__)
+
+/*
+ * AR5K REGISTER ACCESS
+ */
+
+/* Some macros to read/write fields */
+
+/* First shift, then mask */
+#define AR5K_REG_SM(_val, _flags) \
+ (((_val) << _flags##_S) & (_flags))
+
+/* First mask, then shift */
+#define AR5K_REG_MS(_val, _flags) \
+ (((_val) & (_flags)) >> _flags##_S)
+
+/* Some registers can hold multiple values of interest. For this
+ * reason when we want to write to these registers we must first
+ * retrieve the values which we do not want to clear (lets call this
+ * old_data) and then set the register with this and our new_value:
+ * ( old_data | new_value) */
+#define AR5K_REG_WRITE_BITS(ah, _reg, _flags, _val) \
+ ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, _reg) & ~(_flags)) | \
+ (((_val) << _flags##_S) & (_flags)), _reg)
+
+#define AR5K_REG_MASKED_BITS(ah, _reg, _flags, _mask) \
+ ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, _reg) & \
+ (_mask)) | (_flags), _reg)
+
+#define AR5K_REG_ENABLE_BITS(ah, _reg, _flags) \
+ ath5k_hw_reg_write(ah, ath5k_hw_reg_read(ah, _reg) | (_flags), _reg)
+
+#define AR5K_REG_DISABLE_BITS(ah, _reg, _flags) \
+ ath5k_hw_reg_write(ah, ath5k_hw_reg_read(ah, _reg) & ~(_flags), _reg)
+
+/* Access QCU registers per queue */
+#define AR5K_REG_READ_Q(ah, _reg, _queue) \
+ (ath5k_hw_reg_read(ah, _reg) & (1 << _queue)) \
+
+#define AR5K_REG_WRITE_Q(ah, _reg, _queue) \
+ ath5k_hw_reg_write(ah, (1 << _queue), _reg)
+
+#define AR5K_Q_ENABLE_BITS(_reg, _queue) do { \
+ _reg |= 1 << _queue; \
+} while (0)
+
+#define AR5K_Q_DISABLE_BITS(_reg, _queue) do { \
+ _reg &= ~(1 << _queue); \
+} while (0)
+
+/* Used while writing initvals */
+#define AR5K_REG_WAIT(_i) do { \
+ if (_i % 64) \
+ udelay(1); \
+} while (0)
+
+/*
+ * Some tunable values (these should be changeable by the user)
+ * TODO: Make use of them and add more options OR use debug/configfs
+ */
+#define AR5K_TUNE_DMA_BEACON_RESP 2
+#define AR5K_TUNE_SW_BEACON_RESP 10
+#define AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF 0
+#define AR5K_TUNE_MIN_TX_FIFO_THRES 1
+#define AR5K_TUNE_MAX_TX_FIFO_THRES ((IEEE80211_MAX_FRAME_LEN / 64) + 1)
+#define AR5K_TUNE_REGISTER_TIMEOUT 20000
+/* Register for RSSI threshold has a mask of 0xff, so 255 seems to
+ * be the max value. */
+#define AR5K_TUNE_RSSI_THRES 129
+/* This must be set when setting the RSSI threshold otherwise it can
+ * prevent a reset. If AR5K_RSSI_THR is read after writing to it
+ * the BMISS_THRES will be seen as 0, seems hardware doesn't keep
+ * track of it. Max value depends on hardware. For AR5210 this is just 7.
+ * For AR5211+ this seems to be up to 255. */
+#define AR5K_TUNE_BMISS_THRES 7
+#define AR5K_TUNE_REGISTER_DWELL_TIME 20000
+#define AR5K_TUNE_BEACON_INTERVAL 100
+#define AR5K_TUNE_AIFS 2
+#define AR5K_TUNE_AIFS_11B 2
+#define AR5K_TUNE_AIFS_XR 0
+#define AR5K_TUNE_CWMIN 15
+#define AR5K_TUNE_CWMIN_11B 31
+#define AR5K_TUNE_CWMIN_XR 3
+#define AR5K_TUNE_CWMAX 1023
+#define AR5K_TUNE_CWMAX_11B 1023
+#define AR5K_TUNE_CWMAX_XR 7
+#define AR5K_TUNE_NOISE_FLOOR -72
+#define AR5K_TUNE_CCA_MAX_GOOD_VALUE -95
+#define AR5K_TUNE_MAX_TXPOWER 63
+#define AR5K_TUNE_DEFAULT_TXPOWER 25
+#define AR5K_TUNE_TPC_TXPOWER false
+#define ATH5K_TUNE_CALIBRATION_INTERVAL_FULL 10000 /* 10 sec */
+#define ATH5K_TUNE_CALIBRATION_INTERVAL_ANI 1000 /* 1 sec */
+#define ATH5K_TUNE_CALIBRATION_INTERVAL_NF 60000 /* 60 sec */
+
+#define ATH5K_TX_COMPLETE_POLL_INT 3000 /* 3 sec */
+
+#define AR5K_INIT_CARR_SENSE_EN 1
+
+/*Swap RX/TX Descriptor for big endian archs*/
+#if defined(__BIG_ENDIAN)
+#define AR5K_INIT_CFG ( \
+ AR5K_CFG_SWTD | AR5K_CFG_SWRD \
+)
+#else
+#define AR5K_INIT_CFG 0x00000000
+#endif
+
+/* Initial values */
+#define AR5K_INIT_CYCRSSI_THR1 2
+
+/* Tx retry limit defaults from standard */
+#define AR5K_INIT_RETRY_SHORT 7
+#define AR5K_INIT_RETRY_LONG 4
+
+/* Slot time */
+#define AR5K_INIT_SLOT_TIME_TURBO 6
+#define AR5K_INIT_SLOT_TIME_DEFAULT 9
+#define AR5K_INIT_SLOT_TIME_HALF_RATE 13
+#define AR5K_INIT_SLOT_TIME_QUARTER_RATE 21
+#define AR5K_INIT_SLOT_TIME_B 20
+#define AR5K_SLOT_TIME_MAX 0xffff
+
+/* SIFS */
+#define AR5K_INIT_SIFS_TURBO 6
+#define AR5K_INIT_SIFS_DEFAULT_BG 10
+#define AR5K_INIT_SIFS_DEFAULT_A 16
+#define AR5K_INIT_SIFS_HALF_RATE 32
+#define AR5K_INIT_SIFS_QUARTER_RATE 64
+
+/* Used to calculate tx time for non 5/10/40MHz
+ * operation */
+/* It's preamble time + signal time (16 + 4) */
+#define AR5K_INIT_OFDM_PREAMPLE_TIME 20
+/* Preamble time for 40MHz (turbo) operation (min ?) */
+#define AR5K_INIT_OFDM_PREAMBLE_TIME_MIN 14
+#define AR5K_INIT_OFDM_SYMBOL_TIME 4
+#define AR5K_INIT_OFDM_PLCP_BITS 22
+
+/* Rx latency for 5 and 10MHz operation (max ?) */
+#define AR5K_INIT_RX_LAT_MAX 63
+/* Tx latencies from initvals (5212 only but no problem
+ * because we only tweak them on 5212) */
+#define AR5K_INIT_TX_LAT_A 54
+#define AR5K_INIT_TX_LAT_BG 384
+/* Tx latency for 40MHz (turbo) operation (min ?) */
+#define AR5K_INIT_TX_LAT_MIN 32
+/* Default Tx/Rx latencies (same for 5211)*/
+#define AR5K_INIT_TX_LATENCY_5210 54
+#define AR5K_INIT_RX_LATENCY_5210 29
+
+/* Tx frame to Tx data start delay */
+#define AR5K_INIT_TXF2TXD_START_DEFAULT 14
+#define AR5K_INIT_TXF2TXD_START_DELAY_10MHZ 12
+#define AR5K_INIT_TXF2TXD_START_DELAY_5MHZ 13
+
+/* We need to increase PHY switch and agc settling time
+ * on turbo mode */
+#define AR5K_SWITCH_SETTLING 5760
+#define AR5K_SWITCH_SETTLING_TURBO 7168
+
+#define AR5K_AGC_SETTLING 28
+/* 38 on 5210 but shouldn't matter */
+#define AR5K_AGC_SETTLING_TURBO 37
+
+
+/* GENERIC CHIPSET DEFINITIONS */
+
+/* MAC Chips */
+enum ath5k_version {
+ AR5K_AR5210 = 0,
+ AR5K_AR5211 = 1,
+ AR5K_AR5212 = 2,
+};
+
+/* PHY Chips */
+enum ath5k_radio {
+ AR5K_RF5110 = 0,
+ AR5K_RF5111 = 1,
+ AR5K_RF5112 = 2,
+ AR5K_RF2413 = 3,
+ AR5K_RF5413 = 4,
+ AR5K_RF2316 = 5,
+ AR5K_RF2317 = 6,
+ AR5K_RF2425 = 7,
+};
+
+/*
+ * Common silicon revision/version values
+ */
+
+#define AR5K_SREV_UNKNOWN 0xffff
+
+#define AR5K_SREV_AR5210 0x00 /* Crete */
+#define AR5K_SREV_AR5311 0x10 /* Maui 1 */
+#define AR5K_SREV_AR5311A 0x20 /* Maui 2 */
+#define AR5K_SREV_AR5311B 0x30 /* Spirit */
+#define AR5K_SREV_AR5211 0x40 /* Oahu */
+#define AR5K_SREV_AR5212 0x50 /* Venice */
+#define AR5K_SREV_AR5312_R2 0x52 /* AP31 */
+#define AR5K_SREV_AR5212_V4 0x54 /* ??? */
+#define AR5K_SREV_AR5213 0x55 /* ??? */
+#define AR5K_SREV_AR5312_R7 0x57 /* AP30 */
+#define AR5K_SREV_AR2313_R8 0x58 /* AP43 */
+#define AR5K_SREV_AR5213A 0x59 /* Hainan */
+#define AR5K_SREV_AR2413 0x78 /* Griffin lite */
+#define AR5K_SREV_AR2414 0x70 /* Griffin */
+#define AR5K_SREV_AR2315_R6 0x86 /* AP51-Light */
+#define AR5K_SREV_AR2315_R7 0x87 /* AP51-Full */
+#define AR5K_SREV_AR5424 0x90 /* Condor */
+#define AR5K_SREV_AR2317_R1 0x90 /* AP61-Light */
+#define AR5K_SREV_AR2317_R2 0x91 /* AP61-Full */
+#define AR5K_SREV_AR5413 0xa4 /* Eagle lite */
+#define AR5K_SREV_AR5414 0xa0 /* Eagle */
+#define AR5K_SREV_AR2415 0xb0 /* Talon */
+#define AR5K_SREV_AR5416 0xc0 /* PCI-E */
+#define AR5K_SREV_AR5418 0xca /* PCI-E */
+#define AR5K_SREV_AR2425 0xe0 /* Swan */
+#define AR5K_SREV_AR2417 0xf0 /* Nala */
+
+#define AR5K_SREV_RAD_5110 0x00
+#define AR5K_SREV_RAD_5111 0x10
+#define AR5K_SREV_RAD_5111A 0x15
+#define AR5K_SREV_RAD_2111 0x20
+#define AR5K_SREV_RAD_5112 0x30
+#define AR5K_SREV_RAD_5112A 0x35
+#define AR5K_SREV_RAD_5112B 0x36
+#define AR5K_SREV_RAD_2112 0x40
+#define AR5K_SREV_RAD_2112A 0x45
+#define AR5K_SREV_RAD_2112B 0x46
+#define AR5K_SREV_RAD_2413 0x50
+#define AR5K_SREV_RAD_5413 0x60
+#define AR5K_SREV_RAD_2316 0x70 /* Cobra SoC */
+#define AR5K_SREV_RAD_2317 0x80
+#define AR5K_SREV_RAD_5424 0xa0 /* Mostly same as 5413 */
+#define AR5K_SREV_RAD_2425 0xa2
+#define AR5K_SREV_RAD_5133 0xc0
+
+#define AR5K_SREV_PHY_5211 0x30
+#define AR5K_SREV_PHY_5212 0x41
+#define AR5K_SREV_PHY_5212A 0x42
+#define AR5K_SREV_PHY_5212B 0x43
+#define AR5K_SREV_PHY_2413 0x45
+#define AR5K_SREV_PHY_5413 0x61
+#define AR5K_SREV_PHY_2425 0x70
+
+/* TODO add support to mac80211 for vendor-specific rates and modes */
+
+/*
+ * Some of this information is based on Documentation from:
+ *
+ * http://madwifi-project.org/wiki/ChipsetFeatures/SuperAG
+ *
+ * Modulation for Atheros' eXtended Range - range enhancing extension that is
+ * supposed to double the distance an Atheros client device can keep a
+ * connection with an Atheros access point. This is achieved by increasing
+ * the receiver sensitivity up to, -105dBm, which is about 20dB above what
+ * the 802.11 specifications demand. In addition, new (proprietary) data rates
+ * are introduced: 3, 2, 1, 0.5 and 0.25 MBit/s.
+ *
+ * Please note that can you either use XR or TURBO but you cannot use both,
+ * they are exclusive.
+ *
+ */
+#define MODULATION_XR 0x00000200
+/*
+ * Modulation for Atheros' Turbo G and Turbo A, its supposed to provide a
+ * throughput transmission speed up to 40Mbit/s-60Mbit/s at a 108Mbit/s
+ * signaling rate achieved through the bonding of two 54Mbit/s 802.11g
+ * channels. To use this feature your Access Point must also support it.
+ * There is also a distinction between "static" and "dynamic" turbo modes:
+ *
+ * - Static: is the dumb version: devices set to this mode stick to it until
+ * the mode is turned off.
+ * - Dynamic: is the intelligent version, the network decides itself if it
+ * is ok to use turbo. As soon as traffic is detected on adjacent channels
+ * (which would get used in turbo mode), or when a non-turbo station joins
+ * the network, turbo mode won't be used until the situation changes again.
+ * Dynamic mode is achieved by Atheros' Adaptive Radio (AR) feature which
+ * monitors the used radio band in order to decide whether turbo mode may
+ * be used or not.
+ *
+ * This article claims Super G sticks to bonding of channels 5 and 6 for
+ * USA:
+ *
+ * http://www.pcworld.com/article/id,113428-page,1/article.html
+ *
+ * The channel bonding seems to be driver specific though. In addition to
+ * deciding what channels will be used, these "Turbo" modes are accomplished
+ * by also enabling the following features:
+ *
+ * - Bursting: allows multiple frames to be sent at once, rather than pausing
+ * after each frame. Bursting is a standards-compliant feature that can be
+ * used with any Access Point.
+ * - Fast frames: increases the amount of information that can be sent per
+ * frame, also resulting in a reduction of transmission overhead. It is a
+ * proprietary feature that needs to be supported by the Access Point.
+ * - Compression: data frames are compressed in real time using a Lempel Ziv
+ * algorithm. This is done transparently. Once this feature is enabled,
+ * compression and decompression takes place inside the chipset, without
+ * putting additional load on the host CPU.
+ *
+ */
+#define MODULATION_TURBO 0x00000080
+
+enum ath5k_driver_mode {
+ AR5K_MODE_11A = 0,
+ AR5K_MODE_11B = 1,
+ AR5K_MODE_11G = 2,
+ AR5K_MODE_MAX = 3
+};
+
+enum ath5k_ant_mode {
+ AR5K_ANTMODE_DEFAULT = 0, /* default antenna setup */
+ AR5K_ANTMODE_FIXED_A = 1, /* only antenna A is present */
+ AR5K_ANTMODE_FIXED_B = 2, /* only antenna B is present */
+ AR5K_ANTMODE_SINGLE_AP = 3, /* sta locked on a single ap */
+ AR5K_ANTMODE_SECTOR_AP = 4, /* AP with tx antenna set on tx desc */
+ AR5K_ANTMODE_SECTOR_STA = 5, /* STA with tx antenna set on tx desc */
+ AR5K_ANTMODE_DEBUG = 6, /* Debug mode -A -> Rx, B-> Tx- */
+ AR5K_ANTMODE_MAX,
+};
+
+enum ath5k_bw_mode {
+ AR5K_BWMODE_DEFAULT = 0, /* 20MHz, default operation */
+ AR5K_BWMODE_5MHZ = 1, /* Quarter rate */
+ AR5K_BWMODE_10MHZ = 2, /* Half rate */
+ AR5K_BWMODE_40MHZ = 3 /* Turbo */
+};
+
+/****************\
+ TX DEFINITIONS
+\****************/
+
+/*
+ * TX Status descriptor
+ */
+struct ath5k_tx_status {
+ u16 ts_seqnum;
+ u16 ts_tstamp;
+ u8 ts_status;
+ u8 ts_final_idx;
+ u8 ts_final_retry;
+ s8 ts_rssi;
+ u8 ts_shortretry;
+ u8 ts_virtcol;
+ u8 ts_antenna;
+};
+
+#define AR5K_TXSTAT_ALTRATE 0x80
+#define AR5K_TXERR_XRETRY 0x01
+#define AR5K_TXERR_FILT 0x02
+#define AR5K_TXERR_FIFO 0x04
+
+/**
+ * enum ath5k_tx_queue - Queue types used to classify tx queues.
+ * @AR5K_TX_QUEUE_INACTIVE: q is unused -- see ath5k_hw_release_tx_queue
+ * @AR5K_TX_QUEUE_DATA: A normal data queue
+ * @AR5K_TX_QUEUE_XR_DATA: An XR-data queue
+ * @AR5K_TX_QUEUE_BEACON: The beacon queue
+ * @AR5K_TX_QUEUE_CAB: The after-beacon queue
+ * @AR5K_TX_QUEUE_UAPSD: Unscheduled Automatic Power Save Delivery queue
+ */
+enum ath5k_tx_queue {
+ AR5K_TX_QUEUE_INACTIVE = 0,
+ AR5K_TX_QUEUE_DATA,
+ AR5K_TX_QUEUE_XR_DATA,
+ AR5K_TX_QUEUE_BEACON,
+ AR5K_TX_QUEUE_CAB,
+ AR5K_TX_QUEUE_UAPSD,
+};
+
+#define AR5K_NUM_TX_QUEUES 10
+#define AR5K_NUM_TX_QUEUES_NOQCU 2
+
+/*
+ * Queue syb-types to classify normal data queues.
+ * These are the 4 Access Categories as defined in
+ * WME spec. 0 is the lowest priority and 4 is the
+ * highest. Normal data that hasn't been classified
+ * goes to the Best Effort AC.
+ */
+enum ath5k_tx_queue_subtype {
+ AR5K_WME_AC_BK = 0, /*Background traffic*/
+ AR5K_WME_AC_BE, /*Best-effort (normal) traffic*/
+ AR5K_WME_AC_VI, /*Video traffic*/
+ AR5K_WME_AC_VO, /*Voice traffic*/
+};
+
+/*
+ * Queue ID numbers as returned by the hw functions, each number
+ * represents a hw queue. If hw does not support hw queues
+ * (eg 5210) all data goes in one queue. These match
+ * d80211 definitions (net80211/MadWiFi don't use them).
+ */
+enum ath5k_tx_queue_id {
+ AR5K_TX_QUEUE_ID_NOQCU_DATA = 0,
+ AR5K_TX_QUEUE_ID_NOQCU_BEACON = 1,
+ AR5K_TX_QUEUE_ID_DATA_MIN = 0, /*IEEE80211_TX_QUEUE_DATA0*/
+ AR5K_TX_QUEUE_ID_DATA_MAX = 3, /*IEEE80211_TX_QUEUE_DATA3*/
+ AR5K_TX_QUEUE_ID_DATA_SVP = 5, /*IEEE80211_TX_QUEUE_SVP - Spectralink Voice Protocol*/
+ AR5K_TX_QUEUE_ID_CAB = 6, /*IEEE80211_TX_QUEUE_AFTER_BEACON*/
+ AR5K_TX_QUEUE_ID_BEACON = 7, /*IEEE80211_TX_QUEUE_BEACON*/
+ AR5K_TX_QUEUE_ID_UAPSD = 8,
+ AR5K_TX_QUEUE_ID_XR_DATA = 9,
+};
+
+/*
+ * Flags to set hw queue's parameters...
+ */
+#define AR5K_TXQ_FLAG_TXOKINT_ENABLE 0x0001 /* Enable TXOK interrupt */
+#define AR5K_TXQ_FLAG_TXERRINT_ENABLE 0x0002 /* Enable TXERR interrupt */
+#define AR5K_TXQ_FLAG_TXEOLINT_ENABLE 0x0004 /* Enable TXEOL interrupt -not used- */
+#define AR5K_TXQ_FLAG_TXDESCINT_ENABLE 0x0008 /* Enable TXDESC interrupt -not used- */
+#define AR5K_TXQ_FLAG_TXURNINT_ENABLE 0x0010 /* Enable TXURN interrupt */
+#define AR5K_TXQ_FLAG_CBRORNINT_ENABLE 0x0020 /* Enable CBRORN interrupt */
+#define AR5K_TXQ_FLAG_CBRURNINT_ENABLE 0x0040 /* Enable CBRURN interrupt */
+#define AR5K_TXQ_FLAG_QTRIGINT_ENABLE 0x0080 /* Enable QTRIG interrupt */
+#define AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE 0x0100 /* Enable TXNOFRM interrupt */
+#define AR5K_TXQ_FLAG_BACKOFF_DISABLE 0x0200 /* Disable random post-backoff */
+#define AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE 0x0300 /* Enable ready time expiry policy (?)*/
+#define AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE 0x0800 /* Enable backoff while bursting */
+#define AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS 0x1000 /* Disable backoff while bursting */
+#define AR5K_TXQ_FLAG_COMPRESSION_ENABLE 0x2000 /* Enable hw compression -not implemented-*/
+
+/*
+ * Data transmit queue state. One of these exists for each
+ * hardware transmit queue. Packets sent to us from above
+ * are assigned to queues based on their priority. Not all
+ * devices support a complete set of hardware transmit queues.
+ * For those devices the array sc_ac2q will map multiple
+ * priorities to fewer hardware queues (typically all to one
+ * hardware queue).
+ */
+struct ath5k_txq {
+ unsigned int qnum; /* hardware q number */
+ u32 *link; /* link ptr in last TX desc */
+ struct list_head q; /* transmit queue */
+ spinlock_t lock; /* lock on q and link */
+ bool setup;
+ int txq_len; /* number of queued buffers */
+ int txq_max; /* max allowed num of queued buffers */
+ bool txq_poll_mark;
+ unsigned int txq_stuck; /* informational counter */
+};
+
+/*
+ * A struct to hold tx queue's parameters
+ */
+struct ath5k_txq_info {
+ enum ath5k_tx_queue tqi_type;
+ enum ath5k_tx_queue_subtype tqi_subtype;
+ u16 tqi_flags; /* Tx queue flags (see above) */
+ u8 tqi_aifs; /* Arbitrated Interframe Space */
+ u16 tqi_cw_min; /* Minimum Contention Window */
+ u16 tqi_cw_max; /* Maximum Contention Window */
+ u32 tqi_cbr_period; /* Constant bit rate period */
+ u32 tqi_cbr_overflow_limit;
+ u32 tqi_burst_time;
+ u32 tqi_ready_time; /* Time queue waits after an event */
+};
+
+/*
+ * Transmit packet types.
+ * used on tx control descriptor
+ */
+enum ath5k_pkt_type {
+ AR5K_PKT_TYPE_NORMAL = 0,
+ AR5K_PKT_TYPE_ATIM = 1,
+ AR5K_PKT_TYPE_PSPOLL = 2,
+ AR5K_PKT_TYPE_BEACON = 3,
+ AR5K_PKT_TYPE_PROBE_RESP = 4,
+ AR5K_PKT_TYPE_PIFS = 5,
+};
+
+/*
+ * TX power and TPC settings
+ */
+#define AR5K_TXPOWER_OFDM(_r, _v) ( \
+ ((0 & 1) << ((_v) + 6)) | \
+ (((ah->ah_txpower.txp_rates_power_table[(_r)]) & 0x3f) << (_v)) \
+)
+
+#define AR5K_TXPOWER_CCK(_r, _v) ( \
+ (ah->ah_txpower.txp_rates_power_table[(_r)] & 0x3f) << (_v) \
+)
+
+/*
+ * DMA size definitions (2^(n+2))
+ */
+enum ath5k_dmasize {
+ AR5K_DMASIZE_4B = 0,
+ AR5K_DMASIZE_8B,
+ AR5K_DMASIZE_16B,
+ AR5K_DMASIZE_32B,
+ AR5K_DMASIZE_64B,
+ AR5K_DMASIZE_128B,
+ AR5K_DMASIZE_256B,
+ AR5K_DMASIZE_512B
+};
+
+
+/****************\
+ RX DEFINITIONS
+\****************/
+
+/*
+ * RX Status descriptor
+ */
+struct ath5k_rx_status {
+ u16 rs_datalen;
+ u16 rs_tstamp;
+ u8 rs_status;
+ u8 rs_phyerr;
+ s8 rs_rssi;
+ u8 rs_keyix;
+ u8 rs_rate;
+ u8 rs_antenna;
+ u8 rs_more;
+};
+
+#define AR5K_RXERR_CRC 0x01
+#define AR5K_RXERR_PHY 0x02
+#define AR5K_RXERR_FIFO 0x04
+#define AR5K_RXERR_DECRYPT 0x08
+#define AR5K_RXERR_MIC 0x10
+#define AR5K_RXKEYIX_INVALID ((u8) -1)
+#define AR5K_TXKEYIX_INVALID ((u32) -1)
+
+
+/**************************\
+ BEACON TIMERS DEFINITIONS
+\**************************/
+
+#define AR5K_BEACON_PERIOD 0x0000ffff
+#define AR5K_BEACON_ENA 0x00800000 /*enable beacon xmit*/
+#define AR5K_BEACON_RESET_TSF 0x01000000 /*force a TSF reset*/
+
+
+/*
+ * TSF to TU conversion:
+ *
+ * TSF is a 64bit value in usec (microseconds).
+ * TU is a 32bit value and defined by IEEE802.11 (page 6) as "A measurement of
+ * time equal to 1024 usec", so it's roughly milliseconds (usec / 1024).
+ */
+#define TSF_TO_TU(_tsf) (u32)((_tsf) >> 10)
+
+
+/*******************************\
+ GAIN OPTIMIZATION DEFINITIONS
+\*******************************/
+
+enum ath5k_rfgain {
+ AR5K_RFGAIN_INACTIVE = 0,
+ AR5K_RFGAIN_ACTIVE,
+ AR5K_RFGAIN_READ_REQUESTED,
+ AR5K_RFGAIN_NEED_CHANGE,
+};
+
+struct ath5k_gain {
+ u8 g_step_idx;
+ u8 g_current;
+ u8 g_target;
+ u8 g_low;
+ u8 g_high;
+ u8 g_f_corr;
+ u8 g_state;
+};
+
+/********************\
+ COMMON DEFINITIONS
+\********************/
+
+#define AR5K_SLOT_TIME_9 396
+#define AR5K_SLOT_TIME_20 880
+#define AR5K_SLOT_TIME_MAX 0xffff
+
+/*
+ * The following structure is used to map 2GHz channels to
+ * 5GHz Atheros channels.
+ * TODO: Clean up
+ */
+struct ath5k_athchan_2ghz {
+ u32 a2_flags;
+ u16 a2_athchan;
+};
+
+
+/******************\
+ RATE DEFINITIONS
+\******************/
+
+/**
+ * Seems the ar5xxx hardware supports up to 32 rates, indexed by 1-32.
+ *
+ * The rate code is used to get the RX rate or set the TX rate on the
+ * hardware descriptors. It is also used for internal modulation control
+ * and settings.
+ *
+ * This is the hardware rate map we are aware of:
+ *
+ * rate_code 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08
+ * rate_kbps 3000 1000 ? ? ? 2000 500 48000
+ *
+ * rate_code 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10
+ * rate_kbps 24000 12000 6000 54000 36000 18000 9000 ?
+ *
+ * rate_code 17 18 19 20 21 22 23 24
+ * rate_kbps ? ? ? ? ? ? ? 11000
+ *
+ * rate_code 25 26 27 28 29 30 31 32
+ * rate_kbps 5500 2000 1000 11000S 5500S 2000S ? ?
+ *
+ * "S" indicates CCK rates with short preamble.
+ *
+ * AR5211 has different rate codes for CCK (802.11B) rates. It only uses the
+ * lowest 4 bits, so they are the same as below with a 0xF mask.
+ * (0xB, 0xA, 0x9 and 0x8 for 1M, 2M, 5.5M and 11M).
+ * We handle this in ath5k_setup_bands().
+ */
+#define AR5K_MAX_RATES 32
+
+/* B */
+#define ATH5K_RATE_CODE_1M 0x1B
+#define ATH5K_RATE_CODE_2M 0x1A
+#define ATH5K_RATE_CODE_5_5M 0x19
+#define ATH5K_RATE_CODE_11M 0x18
+/* A and G */
+#define ATH5K_RATE_CODE_6M 0x0B
+#define ATH5K_RATE_CODE_9M 0x0F
+#define ATH5K_RATE_CODE_12M 0x0A
+#define ATH5K_RATE_CODE_18M 0x0E
+#define ATH5K_RATE_CODE_24M 0x09
+#define ATH5K_RATE_CODE_36M 0x0D
+#define ATH5K_RATE_CODE_48M 0x08
+#define ATH5K_RATE_CODE_54M 0x0C
+/* XR */
+#define ATH5K_RATE_CODE_XR_500K 0x07
+#define ATH5K_RATE_CODE_XR_1M 0x02
+#define ATH5K_RATE_CODE_XR_2M 0x06
+#define ATH5K_RATE_CODE_XR_3M 0x01
+
+/* adding this flag to rate_code enables short preamble */
+#define AR5K_SET_SHORT_PREAMBLE 0x04
+
+/*
+ * Crypto definitions
+ */
+
+#define AR5K_KEYCACHE_SIZE 8
+extern int ath5k_modparam_nohwcrypt;
+
+/***********************\
+ HW RELATED DEFINITIONS
+\***********************/
+
+/*
+ * Misc definitions
+ */
+#define AR5K_RSSI_EP_MULTIPLIER (1 << 7)
+
+#define AR5K_ASSERT_ENTRY(_e, _s) do { \
+ if (_e >= _s) \
+ return false; \
+} while (0)
+
+/*
+ * Hardware interrupt abstraction
+ */
+
+/**
+ * enum ath5k_int - Hardware interrupt masks helpers
+ *
+ * @AR5K_INT_RX: mask to identify received frame interrupts, of type
+ * AR5K_ISR_RXOK or AR5K_ISR_RXERR
+ * @AR5K_INT_RXDESC: Request RX descriptor/Read RX descriptor (?)
+ * @AR5K_INT_RXNOFRM: No frame received (?)
+ * @AR5K_INT_RXEOL: received End Of List for VEOL (Virtual End Of List). The
+ * Queue Control Unit (QCU) signals an EOL interrupt only if a descriptor's
+ * LinkPtr is NULL. For more details, refer to:
+ * http://www.freepatentsonline.com/20030225739.html
+ * @AR5K_INT_RXORN: Indicates we got RX overrun (eg. no more descriptors).
+ * Note that Rx overrun is not always fatal, on some chips we can continue
+ * operation without resetting the card, that's why int_fatal is not
+ * common for all chips.
+ * @AR5K_INT_TX: mask to identify received frame interrupts, of type
+ * AR5K_ISR_TXOK or AR5K_ISR_TXERR
+ * @AR5K_INT_TXDESC: Request TX descriptor/Read TX status descriptor (?)
+ * @AR5K_INT_TXURN: received when we should increase the TX trigger threshold
+ * We currently do increments on interrupt by
+ * (AR5K_TUNE_MAX_TX_FIFO_THRES - current_trigger_level) / 2
+ * @AR5K_INT_MIB: Indicates the either Management Information Base counters or
+ * one of the PHY error counters reached the maximum value and should be
+ * read and cleared.
+ * @AR5K_INT_RXPHY: RX PHY Error
+ * @AR5K_INT_RXKCM: RX Key cache miss
+ * @AR5K_INT_SWBA: SoftWare Beacon Alert - indicates its time to send a
+ * beacon that must be handled in software. The alternative is if you
+ * have VEOL support, in that case you let the hardware deal with things.
+ * @AR5K_INT_BMISS: If in STA mode this indicates we have stopped seeing
+ * beacons from the AP have associated with, we should probably try to
+ * reassociate. When in IBSS mode this might mean we have not received
+ * any beacons from any local stations. Note that every station in an
+ * IBSS schedules to send beacons at the Target Beacon Transmission Time
+ * (TBTT) with a random backoff.
+ * @AR5K_INT_BNR: Beacon Not Ready interrupt - ??
+ * @AR5K_INT_GPIO: GPIO interrupt is used for RF Kill, disabled for now
+ * until properly handled
+ * @AR5K_INT_FATAL: Fatal errors were encountered, typically caused by DMA
+ * errors. These types of errors we can enable seem to be of type
+ * AR5K_SIMR2_MCABT, AR5K_SIMR2_SSERR and AR5K_SIMR2_DPERR.
+ * @AR5K_INT_GLOBAL: Used to clear and set the IER
+ * @AR5K_INT_NOCARD: signals the card has been removed
+ * @AR5K_INT_COMMON: common interrupts shared among MACs with the same
+ * bit value
+ *
+ * These are mapped to take advantage of some common bits
+ * between the MACs, to be able to set intr properties
+ * easier. Some of them are not used yet inside hw.c. Most map
+ * to the respective hw interrupt value as they are common among different
+ * MACs.
+ */
+enum ath5k_int {
+ AR5K_INT_RXOK = 0x00000001,
+ AR5K_INT_RXDESC = 0x00000002,
+ AR5K_INT_RXERR = 0x00000004,
+ AR5K_INT_RXNOFRM = 0x00000008,
+ AR5K_INT_RXEOL = 0x00000010,
+ AR5K_INT_RXORN = 0x00000020,
+ AR5K_INT_TXOK = 0x00000040,
+ AR5K_INT_TXDESC = 0x00000080,
+ AR5K_INT_TXERR = 0x00000100,
+ AR5K_INT_TXNOFRM = 0x00000200,
+ AR5K_INT_TXEOL = 0x00000400,
+ AR5K_INT_TXURN = 0x00000800,
+ AR5K_INT_MIB = 0x00001000,
+ AR5K_INT_SWI = 0x00002000,
+ AR5K_INT_RXPHY = 0x00004000,
+ AR5K_INT_RXKCM = 0x00008000,
+ AR5K_INT_SWBA = 0x00010000,
+ AR5K_INT_BRSSI = 0x00020000,
+ AR5K_INT_BMISS = 0x00040000,
+ AR5K_INT_FATAL = 0x00080000, /* Non common */
+ AR5K_INT_BNR = 0x00100000, /* Non common */
+ AR5K_INT_TIM = 0x00200000, /* Non common */
+ AR5K_INT_DTIM = 0x00400000, /* Non common */
+ AR5K_INT_DTIM_SYNC = 0x00800000, /* Non common */
+ AR5K_INT_GPIO = 0x01000000,
+ AR5K_INT_BCN_TIMEOUT = 0x02000000, /* Non common */
+ AR5K_INT_CAB_TIMEOUT = 0x04000000, /* Non common */
+ AR5K_INT_RX_DOPPLER = 0x08000000, /* Non common */
+ AR5K_INT_QCBRORN = 0x10000000, /* Non common */
+ AR5K_INT_QCBRURN = 0x20000000, /* Non common */
+ AR5K_INT_QTRIG = 0x40000000, /* Non common */
+ AR5K_INT_GLOBAL = 0x80000000,
+
+ AR5K_INT_TX_ALL = AR5K_INT_TXOK
+ | AR5K_INT_TXDESC
+ | AR5K_INT_TXERR
+ | AR5K_INT_TXEOL
+ | AR5K_INT_TXURN,
+
+ AR5K_INT_RX_ALL = AR5K_INT_RXOK
+ | AR5K_INT_RXDESC
+ | AR5K_INT_RXERR
+ | AR5K_INT_RXNOFRM
+ | AR5K_INT_RXEOL
+ | AR5K_INT_RXORN,
+
+ AR5K_INT_COMMON = AR5K_INT_RXOK
+ | AR5K_INT_RXDESC
+ | AR5K_INT_RXERR
+ | AR5K_INT_RXNOFRM
+ | AR5K_INT_RXEOL
+ | AR5K_INT_RXORN
+ | AR5K_INT_TXOK
+ | AR5K_INT_TXDESC
+ | AR5K_INT_TXERR
+ | AR5K_INT_TXNOFRM
+ | AR5K_INT_TXEOL
+ | AR5K_INT_TXURN
+ | AR5K_INT_MIB
+ | AR5K_INT_SWI
+ | AR5K_INT_RXPHY
+ | AR5K_INT_RXKCM
+ | AR5K_INT_SWBA
+ | AR5K_INT_BRSSI
+ | AR5K_INT_BMISS
+ | AR5K_INT_GPIO
+ | AR5K_INT_GLOBAL,
+
+ AR5K_INT_NOCARD = 0xffffffff
+};
+
+/* mask which calibration is active at the moment */
+enum ath5k_calibration_mask {
+ AR5K_CALIBRATION_FULL = 0x01,
+ AR5K_CALIBRATION_SHORT = 0x02,
+ AR5K_CALIBRATION_ANI = 0x04,
+};
+
+/*
+ * Power management
+ */
+enum ath5k_power_mode {
+ AR5K_PM_UNDEFINED = 0,
+ AR5K_PM_AUTO,
+ AR5K_PM_AWAKE,
+ AR5K_PM_FULL_SLEEP,
+ AR5K_PM_NETWORK_SLEEP,
+};
+
+/*
+ * These match net80211 definitions (not used in
+ * mac80211).
+ * TODO: Clean this up
+ */
+#define AR5K_LED_INIT 0 /*IEEE80211_S_INIT*/
+#define AR5K_LED_SCAN 1 /*IEEE80211_S_SCAN*/
+#define AR5K_LED_AUTH 2 /*IEEE80211_S_AUTH*/
+#define AR5K_LED_ASSOC 3 /*IEEE80211_S_ASSOC*/
+#define AR5K_LED_RUN 4 /*IEEE80211_S_RUN*/
+
+/* GPIO-controlled software LED */
+#define AR5K_SOFTLED_PIN 0
+#define AR5K_SOFTLED_ON 0
+#define AR5K_SOFTLED_OFF 1
+
+
+/* XXX: we *may* move cap_range stuff to struct wiphy */
+struct ath5k_capabilities {
+ /*
+ * Supported PHY modes
+ * (ie. AR5K_MODE_11A, AR5K_MODE_11B, ...)
+ */
+ DECLARE_BITMAP(cap_mode, AR5K_MODE_MAX);
+
+ /*
+ * Frequency range (without regulation restrictions)
+ */
+ struct {
+ u16 range_2ghz_min;
+ u16 range_2ghz_max;
+ u16 range_5ghz_min;
+ u16 range_5ghz_max;
+ } cap_range;
+
+ /*
+ * Values stored in the EEPROM (some of them...)
+ */
+ struct ath5k_eeprom_info cap_eeprom;
+
+ /*
+ * Queue information
+ */
+ struct {
+ u8 q_tx_num;
+ } cap_queues;
+
+ bool cap_has_phyerr_counters;
+};
+
+/* size of noise floor history (keep it a power of two) */
+#define ATH5K_NF_CAL_HIST_MAX 8
+struct ath5k_nfcal_hist {
+ s16 index; /* current index into nfval */
+ s16 nfval[ATH5K_NF_CAL_HIST_MAX]; /* last few noise floors */
+};
+
+#define ATH5K_LED_MAX_NAME_LEN 31
+
+/*
+ * State for LED triggers
+ */
+struct ath5k_led {
+ char name[ATH5K_LED_MAX_NAME_LEN + 1]; /* name of the LED in sysfs */
+ struct ath5k_hw *ah; /* driver state */
+ struct led_classdev led_dev; /* led classdev */
+};
+
+/* Rfkill */
+struct ath5k_rfkill {
+ /* GPIO PIN for rfkill */
+ u16 gpio;
+ /* polarity of rfkill GPIO PIN */
+ bool polarity;
+ /* RFKILL toggle tasklet */
+ struct tasklet_struct toggleq;
+};
+
+/* statistics */
+struct ath5k_statistics {
+ /* antenna use */
+ unsigned int antenna_rx[5]; /* frames count per antenna RX */
+ unsigned int antenna_tx[5]; /* frames count per antenna TX */
+
+ /* frame errors */
+ unsigned int rx_all_count; /* all RX frames, including errors */
+ unsigned int tx_all_count; /* all TX frames, including errors */
+ unsigned int rx_bytes_count; /* all RX bytes, including errored pkts
+ * and the MAC headers for each packet
+ */
+ unsigned int tx_bytes_count; /* all TX bytes, including errored pkts
+ * and the MAC headers and padding for
+ * each packet.
+ */
+ unsigned int rxerr_crc;
+ unsigned int rxerr_phy;
+ unsigned int rxerr_phy_code[32];
+ unsigned int rxerr_fifo;
+ unsigned int rxerr_decrypt;
+ unsigned int rxerr_mic;
+ unsigned int rxerr_proc;
+ unsigned int rxerr_jumbo;
+ unsigned int txerr_retry;
+ unsigned int txerr_fifo;
+ unsigned int txerr_filt;
+
+ /* MIB counters */
+ unsigned int ack_fail;
+ unsigned int rts_fail;
+ unsigned int rts_ok;
+ unsigned int fcs_error;
+ unsigned int beacons;
+
+ unsigned int mib_intr;
+ unsigned int rxorn_intr;
+ unsigned int rxeol_intr;
+};
+
+/*
+ * Misc defines
+ */
+
+#define AR5K_MAX_GPIO 10
+#define AR5K_MAX_RF_BANKS 8
+
+#if CHAN_DEBUG
+#define ATH_CHAN_MAX (26 + 26 + 26 + 200 + 200)
+#else
+#define ATH_CHAN_MAX (14 + 14 + 14 + 252 + 20)
+#endif
+
+#define ATH_RXBUF 40 /* number of RX buffers */
+#define ATH_TXBUF 200 /* number of TX buffers */
+#define ATH_BCBUF 4 /* number of beacon buffers */
+#define ATH5K_TXQ_LEN_MAX (ATH_TXBUF / 4) /* bufs per queue */
+#define ATH5K_TXQ_LEN_LOW (ATH5K_TXQ_LEN_MAX / 2) /* low mark */
+
+/* Driver state associated with an instance of a device */
+struct ath5k_hw {
+ struct ath_common common;
+
+ struct pci_dev *pdev;
+ struct device *dev; /* for dma mapping */
+ int irq;
+ u16 devid;
+ void __iomem *iobase; /* address of the device */
+ struct mutex lock; /* dev-level lock */
+ struct ieee80211_hw *hw; /* IEEE 802.11 common */
+ struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
+ struct ieee80211_channel channels[ATH_CHAN_MAX];
+ struct ieee80211_rate rates[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
+ s8 rate_idx[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
+ enum nl80211_iftype opmode;
+
+#ifdef CONFIG_ATH5K_DEBUG
+ struct ath5k_dbg_info debug; /* debug info */
+#endif /* CONFIG_ATH5K_DEBUG */
+
+ struct ath5k_buf *bufptr; /* allocated buffer ptr */
+ struct ath5k_desc *desc; /* TX/RX descriptors */
+ dma_addr_t desc_daddr; /* DMA (physical) address */
+ size_t desc_len; /* size of TX/RX descriptors */
+
+ DECLARE_BITMAP(status, 6);
+#define ATH_STAT_INVALID 0 /* disable hardware accesses */
+#define ATH_STAT_MRRETRY 1 /* multi-rate retry support */
+#define ATH_STAT_PROMISC 2
+#define ATH_STAT_LEDSOFT 3 /* enable LED gpio status */
+#define ATH_STAT_STARTED 4 /* opened & irqs enabled */
+#define ATH_STAT_2G_DISABLED 5 /* multiband radio without 2G */
+
+ unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */
+ struct ieee80211_channel *curchan; /* current h/w channel */
+
+ u16 nvifs;
+
+ enum ath5k_int imask; /* interrupt mask copy */
+
+ spinlock_t irqlock;
+ bool rx_pending; /* rx tasklet pending */
+ bool tx_pending; /* tx tasklet pending */
+
+ u8 bssidmask[ETH_ALEN];
+
+ unsigned int led_pin, /* GPIO pin for driving LED */
+ led_on; /* pin setting for LED on */
+
+ struct work_struct reset_work; /* deferred chip reset */
+
+ struct list_head rxbuf; /* receive buffer */
+ spinlock_t rxbuflock;
+ u32 *rxlink; /* link ptr in last RX desc */
+ struct tasklet_struct rxtq; /* rx intr tasklet */
+ struct ath5k_led rx_led; /* rx led */
+
+ struct list_head txbuf; /* transmit buffer */
+ spinlock_t txbuflock;
+ unsigned int txbuf_len; /* buf count in txbuf list */
+ struct ath5k_txq txqs[AR5K_NUM_TX_QUEUES]; /* tx queues */
+ struct tasklet_struct txtq; /* tx intr tasklet */
+ struct ath5k_led tx_led; /* tx led */
+
+ struct ath5k_rfkill rf_kill;
+
+ struct tasklet_struct calib; /* calibration tasklet */
+
+ spinlock_t block; /* protects beacon */
+ struct tasklet_struct beacontq; /* beacon intr tasklet */
+ struct list_head bcbuf; /* beacon buffer */
+ struct ieee80211_vif *bslot[ATH_BCBUF];
+ u16 num_ap_vifs;
+ u16 num_adhoc_vifs;
+ unsigned int bhalq, /* SW q for outgoing beacons */
+ bmisscount, /* missed beacon transmits */
+ bintval, /* beacon interval in TU */
+ bsent;
+ unsigned int nexttbtt; /* next beacon time in TU */
+ struct ath5k_txq *cabq; /* content after beacon */
+
+ int power_level; /* Requested tx power in dBm */
+ bool assoc; /* associate state */
+ bool enable_beacon; /* true if beacons are on */
+
+ struct ath5k_statistics stats;
+
+ struct ath5k_ani_state ani_state;
+ struct tasklet_struct ani_tasklet; /* ANI calibration */
+
+ struct delayed_work tx_complete_work;
+
+ struct survey_info survey; /* collected survey info */
+
+ enum ath5k_int ah_imr;
+
+ struct ieee80211_channel *ah_current_channel;
+ bool ah_calibration;
+ bool ah_single_chip;
+
+ enum ath5k_version ah_version;
+ enum ath5k_radio ah_radio;
+ u32 ah_mac_srev;
+ u16 ah_mac_version;
+ u16 ah_phy_revision;
+ u16 ah_radio_5ghz_revision;
+ u16 ah_radio_2ghz_revision;
+
+#define ah_modes ah_capabilities.cap_mode
+#define ah_ee_version ah_capabilities.cap_eeprom.ee_version
+
+ u8 ah_retry_long;
+ u8 ah_retry_short;
+
+ u32 ah_use_32khz_clock;
+
+ u8 ah_coverage_class;
+ bool ah_ack_bitrate_high;
+ u8 ah_bwmode;
+ bool ah_short_slot;
+
+ /* Antenna Control */
+ u32 ah_ant_ctl[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX];
+ u8 ah_ant_mode;
+ u8 ah_tx_ant;
+ u8 ah_def_ant;
+
+ struct ath5k_capabilities ah_capabilities;
+
+ struct ath5k_txq_info ah_txq[AR5K_NUM_TX_QUEUES];
+ u32 ah_txq_status;
+ u32 ah_txq_imr_txok;
+ u32 ah_txq_imr_txerr;
+ u32 ah_txq_imr_txurn;
+ u32 ah_txq_imr_txdesc;
+ u32 ah_txq_imr_txeol;
+ u32 ah_txq_imr_cbrorn;
+ u32 ah_txq_imr_cbrurn;
+ u32 ah_txq_imr_qtrig;
+ u32 ah_txq_imr_nofrm;
+ u32 ah_txq_isr;
+ u32 *ah_rf_banks;
+ size_t ah_rf_banks_size;
+ size_t ah_rf_regs_count;
+ struct ath5k_gain ah_gain;
+ u8 ah_offset[AR5K_MAX_RF_BANKS];
+
+
+ struct {
+ /* Temporary tables used for interpolation */
+ u8 tmpL[AR5K_EEPROM_N_PD_GAINS]
+ [AR5K_EEPROM_POWER_TABLE_SIZE];
+ u8 tmpR[AR5K_EEPROM_N_PD_GAINS]
+ [AR5K_EEPROM_POWER_TABLE_SIZE];
+ u8 txp_pd_table[AR5K_EEPROM_POWER_TABLE_SIZE * 2];
+ u16 txp_rates_power_table[AR5K_MAX_RATES];
+ u8 txp_min_idx;
+ bool txp_tpc;
+ /* Values in 0.25dB units */
+ s16 txp_min_pwr;
+ s16 txp_max_pwr;
+ s16 txp_cur_pwr;
+ /* Values in 0.5dB units */
+ s16 txp_offset;
+ s16 txp_ofdm;
+ s16 txp_cck_ofdm_gainf_delta;
+ /* Value in dB units */
+ s16 txp_cck_ofdm_pwr_delta;
+ bool txp_setup;
+ } ah_txpower;
+
+ struct ath5k_nfcal_hist ah_nfcal_hist;
+
+ /* average beacon RSSI in our BSS (used by ANI) */
+ struct ewma ah_beacon_rssi_avg;
+
+ /* noise floor from last periodic calibration */
+ s32 ah_noise_floor;
+
+ /* Calibration timestamp */
+ unsigned long ah_cal_next_full;
+ unsigned long ah_cal_next_ani;
+ unsigned long ah_cal_next_nf;
+
+ /* Calibration mask */
+ u8 ah_cal_mask;
+
+ /*
+ * Function pointers
+ */
+ int (*ah_setup_tx_desc)(struct ath5k_hw *, struct ath5k_desc *,
+ unsigned int, unsigned int, int, enum ath5k_pkt_type,
+ unsigned int, unsigned int, unsigned int, unsigned int,
+ unsigned int, unsigned int, unsigned int, unsigned int);
+ int (*ah_proc_tx_desc)(struct ath5k_hw *, struct ath5k_desc *,
+ struct ath5k_tx_status *);
+ int (*ah_proc_rx_desc)(struct ath5k_hw *, struct ath5k_desc *,
+ struct ath5k_rx_status *);
+};
+
+struct ath_bus_ops {
+ enum ath_bus_type ath_bus_type;
+ void (*read_cachesize)(struct ath_common *common, int *csz);
+ bool (*eeprom_read)(struct ath_common *common, u32 off, u16 *data);
+ int (*eeprom_read_mac)(struct ath5k_hw *ah, u8 *mac);
+};
+
+/*
+ * Prototypes
+ */
+extern const struct ieee80211_ops ath5k_hw_ops;
+
+/* Initialization and detach functions */
+int ath5k_hw_init(struct ath5k_hw *ah);
+void ath5k_hw_deinit(struct ath5k_hw *ah);
+
+int ath5k_sysfs_register(struct ath5k_hw *ah);
+void ath5k_sysfs_unregister(struct ath5k_hw *ah);
+
+/*Chip id helper functions */
+int ath5k_hw_read_srev(struct ath5k_hw *ah);
+
+/* LED functions */
+int ath5k_init_leds(struct ath5k_hw *ah);
+void ath5k_led_enable(struct ath5k_hw *ah);
+void ath5k_led_off(struct ath5k_hw *ah);
+void ath5k_unregister_leds(struct ath5k_hw *ah);
+
+
+/* Reset Functions */
+int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel);
+int ath5k_hw_on_hold(struct ath5k_hw *ah);
+int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
+ struct ieee80211_channel *channel, bool fast, bool skip_pcu);
+int ath5k_hw_register_timeout(struct ath5k_hw *ah, u32 reg, u32 flag, u32 val,
+ bool is_set);
+/* Power management functions */
+
+
+/* Clock rate related functions */
+unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec);
+unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock);
+void ath5k_hw_set_clockrate(struct ath5k_hw *ah);
+
+
+/* DMA Related Functions */
+void ath5k_hw_start_rx_dma(struct ath5k_hw *ah);
+u32 ath5k_hw_get_rxdp(struct ath5k_hw *ah);
+int ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr);
+int ath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue);
+int ath5k_hw_stop_beacon_queue(struct ath5k_hw *ah, unsigned int queue);
+u32 ath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue);
+int ath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue,
+ u32 phys_addr);
+int ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase);
+/* Interrupt handling */
+bool ath5k_hw_is_intr_pending(struct ath5k_hw *ah);
+int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask);
+enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask);
+void ath5k_hw_update_mib_counters(struct ath5k_hw *ah);
+/* Init/Stop functions */
+void ath5k_hw_dma_init(struct ath5k_hw *ah);
+int ath5k_hw_dma_stop(struct ath5k_hw *ah);
+
+/* EEPROM access functions */
+int ath5k_eeprom_init(struct ath5k_hw *ah);
+void ath5k_eeprom_detach(struct ath5k_hw *ah);
+
+
+/* Protocol Control Unit Functions */
+/* Helpers */
+int ath5k_hw_get_frame_duration(struct ath5k_hw *ah,
+ int len, struct ieee80211_rate *rate, bool shortpre);
+unsigned int ath5k_hw_get_default_slottime(struct ath5k_hw *ah);
+unsigned int ath5k_hw_get_default_sifs(struct ath5k_hw *ah);
+int ath5k_hw_set_opmode(struct ath5k_hw *ah, enum nl80211_iftype opmode);
+void ath5k_hw_set_coverage_class(struct ath5k_hw *ah, u8 coverage_class);
+/* RX filter control*/
+int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac);
+void ath5k_hw_set_bssid(struct ath5k_hw *ah);
+void ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask);
+void ath5k_hw_set_mcast_filter(struct ath5k_hw *ah, u32 filter0, u32 filter1);
+u32 ath5k_hw_get_rx_filter(struct ath5k_hw *ah);
+void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter);
+/* Receive (DRU) start/stop functions */
+void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah);
+void ath5k_hw_stop_rx_pcu(struct ath5k_hw *ah);
+/* Beacon control functions */
+u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah);
+void ath5k_hw_set_tsf64(struct ath5k_hw *ah, u64 tsf64);
+void ath5k_hw_reset_tsf(struct ath5k_hw *ah);
+void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval);
+bool ath5k_hw_check_beacon_timers(struct ath5k_hw *ah, int intval);
+/* Init function */
+void ath5k_hw_pcu_init(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
+ u8 mode);
+
+/* Queue Control Unit, DFS Control Unit Functions */
+int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue,
+ struct ath5k_txq_info *queue_info);
+int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue,
+ const struct ath5k_txq_info *queue_info);
+int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah,
+ enum ath5k_tx_queue queue_type,
+ struct ath5k_txq_info *queue_info);
+void ath5k_hw_set_tx_retry_limits(struct ath5k_hw *ah,
+ unsigned int queue);
+u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue);
+void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue);
+int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue);
+int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time);
+/* Init function */
+int ath5k_hw_init_queues(struct ath5k_hw *ah);
+
+/* Hardware Descriptor Functions */
+int ath5k_hw_init_desc_functions(struct ath5k_hw *ah);
+int ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
+ u32 size, unsigned int flags);
+int ath5k_hw_setup_mrr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
+ unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2,
+ u_int tx_tries2, unsigned int tx_rate3, u_int tx_tries3);
+
+
+/* GPIO Functions */
+void ath5k_hw_set_ledstate(struct ath5k_hw *ah, unsigned int state);
+int ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio);
+int ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio);
+u32 ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio);
+int ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val);
+void ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio,
+ u32 interrupt_level);
+
+
+/* RFkill Functions */
+void ath5k_rfkill_hw_start(struct ath5k_hw *ah);
+void ath5k_rfkill_hw_stop(struct ath5k_hw *ah);
+
+
+/* Misc functions TODO: Cleanup */
+int ath5k_hw_set_capabilities(struct ath5k_hw *ah);
+int ath5k_hw_enable_pspoll(struct ath5k_hw *ah, u8 *bssid, u16 assoc_id);
+int ath5k_hw_disable_pspoll(struct ath5k_hw *ah);
+
+
+/* Initial register settings functions */
+int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel);
+
+
+/* PHY functions */
+/* Misc PHY functions */
+u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, enum ieee80211_band band);
+int ath5k_hw_phy_disable(struct ath5k_hw *ah);
+/* Gain_F optimization */
+enum ath5k_rfgain ath5k_hw_gainf_calibrate(struct ath5k_hw *ah);
+int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah);
+/* PHY/RF channel functions */
+bool ath5k_channel_ok(struct ath5k_hw *ah, struct ieee80211_channel *channel);
+/* PHY calibration */
+void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah);
+int ath5k_hw_phy_calibrate(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel);
+void ath5k_hw_update_noise_floor(struct ath5k_hw *ah);
+/* Spur mitigation */
+bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel);
+/* Antenna control */
+void ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode);
+void ath5k_hw_set_antenna_switch(struct ath5k_hw *ah, u8 ee_mode);
+/* TX power setup */
+int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower);
+/* Init function */
+int ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
+ u8 mode, bool fast);
+
+/*
+ * Functions used internally
+ */
+
+static inline struct ath_common *ath5k_hw_common(struct ath5k_hw *ah)
+{
+ return &ah->common;
+}
+
+static inline struct ath_regulatory *ath5k_hw_regulatory(struct ath5k_hw *ah)
+{
+ return &(ath5k_hw_common(ah)->regulatory);
+}
+
+#ifdef CONFIG_ATHEROS_AR231X
+#define AR5K_AR2315_PCI_BASE ((void __iomem *)0xb0100000)
+
+static inline void __iomem *ath5k_ahb_reg(struct ath5k_hw *ah, u16 reg)
+{
+ /* On AR2315 and AR2317 the PCI clock domain registers
+ * are outside of the WMAC register space */
+ if (unlikely((reg >= 0x4000) && (reg < 0x5000) &&
+ (ah->ah_mac_srev >= AR5K_SREV_AR2315_R6)))
+ return AR5K_AR2315_PCI_BASE + reg;
+
+ return ah->iobase + reg;
+}
+
+static inline u32 ath5k_hw_reg_read(struct ath5k_hw *ah, u16 reg)
+{
+ return __raw_readl(ath5k_ahb_reg(ah, reg));
+}
+
+static inline void ath5k_hw_reg_write(struct ath5k_hw *ah, u32 val, u16 reg)
+{
+ __raw_writel(val, ath5k_ahb_reg(ah, reg));
+}
+
+#else
+
+static inline u32 ath5k_hw_reg_read(struct ath5k_hw *ah, u16 reg)
+{
+ return ioread32(ah->iobase + reg);
+}
+
+static inline void ath5k_hw_reg_write(struct ath5k_hw *ah, u32 val, u16 reg)
+{
+ iowrite32(val, ah->iobase + reg);
+}
+
+#endif
+
+static inline enum ath_bus_type ath5k_get_bus_type(struct ath5k_hw *ah)
+{
+ return ath5k_hw_common(ah)->bus_ops->ath_bus_type;
+}
+
+static inline void ath5k_read_cachesize(struct ath_common *common, int *csz)
+{
+ common->bus_ops->read_cachesize(common, csz);
+}
+
+static inline bool ath5k_hw_nvram_read(struct ath5k_hw *ah, u32 off, u16 *data)
+{
+ struct ath_common *common = ath5k_hw_common(ah);
+ return common->bus_ops->eeprom_read(common, off, data);
+}
+
+static inline u32 ath5k_hw_bitswap(u32 val, unsigned int bits)
+{
+ u32 retval = 0, bit, i;
+
+ for (i = 0; i < bits; i++) {
+ bit = (val >> i) & 1;
+ retval = (retval << 1) | bit;
+ }
+
+ return retval;
+}
+
+#endif
diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c
new file mode 100644
index 0000000..91627dd
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/attach.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ */
+
+/*************************************\
+* Attach/Detach Functions and helpers *
+\*************************************/
+
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include "ath5k.h"
+#include "reg.h"
+#include "debug.h"
+
+/**
+ * ath5k_hw_post - Power On Self Test helper function
+ *
+ * @ah: The &struct ath5k_hw
+ */
+static int ath5k_hw_post(struct ath5k_hw *ah)
+{
+
+ static const u32 static_pattern[4] = {
+ 0x55555555, 0xaaaaaaaa,
+ 0x66666666, 0x99999999
+ };
+ static const u16 regs[2] = { AR5K_STA_ID0, AR5K_PHY(8) };
+ int i, c;
+ u16 cur_reg;
+ u32 var_pattern;
+ u32 init_val;
+ u32 cur_val;
+
+ for (c = 0; c < 2; c++) {
+
+ cur_reg = regs[c];
+
+ /* Save previous value */
+ init_val = ath5k_hw_reg_read(ah, cur_reg);
+
+ for (i = 0; i < 256; i++) {
+ var_pattern = i << 16 | i;
+ ath5k_hw_reg_write(ah, var_pattern, cur_reg);
+ cur_val = ath5k_hw_reg_read(ah, cur_reg);
+
+ if (cur_val != var_pattern) {
+ ATH5K_ERR(ah, "POST Failed !!!\n");
+ return -EAGAIN;
+ }
+
+ /* Found on ndiswrapper dumps */
+ var_pattern = 0x0039080f;
+ ath5k_hw_reg_write(ah, var_pattern, cur_reg);
+ }
+
+ for (i = 0; i < 4; i++) {
+ var_pattern = static_pattern[i];
+ ath5k_hw_reg_write(ah, var_pattern, cur_reg);
+ cur_val = ath5k_hw_reg_read(ah, cur_reg);
+
+ if (cur_val != var_pattern) {
+ ATH5K_ERR(ah, "POST Failed !!!\n");
+ return -EAGAIN;
+ }
+
+ /* Found on ndiswrapper dumps */
+ var_pattern = 0x003b080f;
+ ath5k_hw_reg_write(ah, var_pattern, cur_reg);
+ }
+
+ /* Restore previous value */
+ ath5k_hw_reg_write(ah, init_val, cur_reg);
+
+ }
+
+ return 0;
+
+}
+
+/**
+ * ath5k_hw_init - Check if hw is supported and init the needed structs
+ *
+ * @ah: The &struct ath5k_hw associated with the device
+ *
+ * Check if the device is supported, perform a POST and initialize the needed
+ * structs. Returns -ENOMEM if we don't have memory for the needed structs,
+ * -ENODEV if the device is not supported or prints an error msg if something
+ * else went wrong.
+ */
+int ath5k_hw_init(struct ath5k_hw *ah)
+{
+ static const u8 zero_mac[ETH_ALEN] = { };
+ struct ath_common *common = ath5k_hw_common(ah);
+ struct pci_dev *pdev = ah->pdev;
+ struct ath5k_eeprom_info *ee;
+ int ret;
+ u32 srev;
+
+ /*
+ * HW information
+ */
+ ah->ah_bwmode = AR5K_BWMODE_DEFAULT;
+ ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
+ ah->ah_imr = 0;
+ ah->ah_retry_short = AR5K_INIT_RETRY_SHORT;
+ ah->ah_retry_long = AR5K_INIT_RETRY_LONG;
+ ah->ah_ant_mode = AR5K_ANTMODE_DEFAULT;
+ ah->ah_noise_floor = -95; /* until first NF calibration is run */
+ ah->ani_state.ani_mode = ATH5K_ANI_MODE_AUTO;
+ ah->ah_current_channel = &ah->channels[0];
+
+ /*
+ * Find the mac version
+ */
+ ath5k_hw_read_srev(ah);
+ srev = ah->ah_mac_srev;
+ if (srev < AR5K_SREV_AR5311)
+ ah->ah_version = AR5K_AR5210;
+ else if (srev < AR5K_SREV_AR5212)
+ ah->ah_version = AR5K_AR5211;
+ else
+ ah->ah_version = AR5K_AR5212;
+
+ /* Get the MAC version */
+ ah->ah_mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER);
+
+ /* Fill the ath5k_hw struct with the needed functions */
+ ret = ath5k_hw_init_desc_functions(ah);
+ if (ret)
+ goto err;
+
+ /* Bring device out of sleep and reset its units */
+ ret = ath5k_hw_nic_wakeup(ah, NULL);
+ if (ret)
+ goto err;
+
+ /* Get PHY and RADIO revisions */
+ ah->ah_phy_revision = ath5k_hw_reg_read(ah, AR5K_PHY_CHIP_ID) &
+ 0xffffffff;
+ ah->ah_radio_5ghz_revision = ath5k_hw_radio_revision(ah,
+ IEEE80211_BAND_5GHZ);
+
+ /* Try to identify radio chip based on its srev */
+ switch (ah->ah_radio_5ghz_revision & 0xf0) {
+ case AR5K_SREV_RAD_5111:
+ ah->ah_radio = AR5K_RF5111;
+ ah->ah_single_chip = false;
+ ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
+ IEEE80211_BAND_2GHZ);
+ break;
+ case AR5K_SREV_RAD_5112:
+ case AR5K_SREV_RAD_2112:
+ ah->ah_radio = AR5K_RF5112;
+ ah->ah_single_chip = false;
+ ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
+ IEEE80211_BAND_2GHZ);
+ break;
+ case AR5K_SREV_RAD_2413:
+ ah->ah_radio = AR5K_RF2413;
+ ah->ah_single_chip = true;
+ break;
+ case AR5K_SREV_RAD_5413:
+ ah->ah_radio = AR5K_RF5413;
+ ah->ah_single_chip = true;
+ break;
+ case AR5K_SREV_RAD_2316:
+ ah->ah_radio = AR5K_RF2316;
+ ah->ah_single_chip = true;
+ break;
+ case AR5K_SREV_RAD_2317:
+ ah->ah_radio = AR5K_RF2317;
+ ah->ah_single_chip = true;
+ break;
+ case AR5K_SREV_RAD_5424:
+ if (ah->ah_mac_version == AR5K_SREV_AR2425 ||
+ ah->ah_mac_version == AR5K_SREV_AR2417) {
+ ah->ah_radio = AR5K_RF2425;
+ ah->ah_single_chip = true;
+ } else {
+ ah->ah_radio = AR5K_RF5413;
+ ah->ah_single_chip = true;
+ }
+ break;
+ default:
+ /* Identify radio based on mac/phy srev */
+ if (ah->ah_version == AR5K_AR5210) {
+ ah->ah_radio = AR5K_RF5110;
+ ah->ah_single_chip = false;
+ } else if (ah->ah_version == AR5K_AR5211) {
+ ah->ah_radio = AR5K_RF5111;
+ ah->ah_single_chip = false;
+ ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
+ IEEE80211_BAND_2GHZ);
+ } else if (ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4) ||
+ ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4) ||
+ ah->ah_phy_revision == AR5K_SREV_PHY_2425) {
+ ah->ah_radio = AR5K_RF2425;
+ ah->ah_single_chip = true;
+ ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2425;
+ } else if (srev == AR5K_SREV_AR5213A &&
+ ah->ah_phy_revision == AR5K_SREV_PHY_5212B) {
+ ah->ah_radio = AR5K_RF5112;
+ ah->ah_single_chip = false;
+ ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_5112B;
+ } else if (ah->ah_mac_version == (AR5K_SREV_AR2415 >> 4) ||
+ ah->ah_mac_version == (AR5K_SREV_AR2315_R6 >> 4)) {
+ ah->ah_radio = AR5K_RF2316;
+ ah->ah_single_chip = true;
+ ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2316;
+ } else if (ah->ah_mac_version == (AR5K_SREV_AR5414 >> 4) ||
+ ah->ah_phy_revision == AR5K_SREV_PHY_5413) {
+ ah->ah_radio = AR5K_RF5413;
+ ah->ah_single_chip = true;
+ ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_5413;
+ } else if (ah->ah_mac_version == (AR5K_SREV_AR2414 >> 4) ||
+ ah->ah_phy_revision == AR5K_SREV_PHY_2413) {
+ ah->ah_radio = AR5K_RF2413;
+ ah->ah_single_chip = true;
+ ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2413;
+ } else {
+ ATH5K_ERR(ah, "Couldn't identify radio revision.\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ }
+
+
+ /* Return on unsupported chips (unsupported eeprom etc) */
+ if ((srev >= AR5K_SREV_AR5416) && (srev < AR5K_SREV_AR2425)) {
+ ATH5K_ERR(ah, "Device not yet supported.\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /*
+ * POST
+ */
+ ret = ath5k_hw_post(ah);
+ if (ret)
+ goto err;
+
+ /* Enable pci core retry fix on Hainan (5213A) and later chips */
+ if (srev >= AR5K_SREV_AR5213A)
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_RETRY_FIX);
+
+ /*
+ * Get card capabilities, calibration values etc
+ * TODO: EEPROM work
+ */
+ ret = ath5k_eeprom_init(ah);
+ if (ret) {
+ ATH5K_ERR(ah, "unable to init EEPROM\n");
+ goto err;
+ }
+
+ ee = &ah->ah_capabilities.cap_eeprom;
+
+ /*
+ * Write PCI-E power save settings
+ */
+ if ((ah->ah_version == AR5K_AR5212) && pdev && (pci_is_pcie(pdev))) {
+ ath5k_hw_reg_write(ah, 0x9248fc00, AR5K_PCIE_SERDES);
+ ath5k_hw_reg_write(ah, 0x24924924, AR5K_PCIE_SERDES);
+
+ /* Shut off RX when elecidle is asserted */
+ ath5k_hw_reg_write(ah, 0x28000039, AR5K_PCIE_SERDES);
+ ath5k_hw_reg_write(ah, 0x53160824, AR5K_PCIE_SERDES);
+
+ /* If serdes programming is enabled, increase PCI-E
+ * tx power for systems with long trace from host
+ * to minicard connector. */
+ if (ee->ee_serdes)
+ ath5k_hw_reg_write(ah, 0xe5980579, AR5K_PCIE_SERDES);
+ else
+ ath5k_hw_reg_write(ah, 0xf6800579, AR5K_PCIE_SERDES);
+
+ /* Shut off PLL and CLKREQ active in L1 */
+ ath5k_hw_reg_write(ah, 0x001defff, AR5K_PCIE_SERDES);
+
+ /* Preserve other settings */
+ ath5k_hw_reg_write(ah, 0x1aaabe40, AR5K_PCIE_SERDES);
+ ath5k_hw_reg_write(ah, 0xbe105554, AR5K_PCIE_SERDES);
+ ath5k_hw_reg_write(ah, 0x000e3007, AR5K_PCIE_SERDES);
+
+ /* Reset SERDES to load new settings */
+ ath5k_hw_reg_write(ah, 0x00000000, AR5K_PCIE_SERDES_RESET);
+ mdelay(1);
+ }
+
+ /* Get misc capabilities */
+ ret = ath5k_hw_set_capabilities(ah);
+ if (ret) {
+ ATH5K_ERR(ah, "unable to get device capabilities\n");
+ goto err;
+ }
+
+ if (test_bit(ATH_STAT_2G_DISABLED, ah->status)) {
+ __clear_bit(AR5K_MODE_11B, ah->ah_capabilities.cap_mode);
+ __clear_bit(AR5K_MODE_11G, ah->ah_capabilities.cap_mode);
+ }
+
+ /* Crypto settings */
+ common->keymax = (ah->ah_version == AR5K_AR5210 ?
+ AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211);
+
+ if (srev >= AR5K_SREV_AR5212_V4 &&
+ (ee->ee_version < AR5K_EEPROM_VERSION_5_0 ||
+ !AR5K_EEPROM_AES_DIS(ee->ee_misc5)))
+ common->crypt_caps |= ATH_CRYPT_CAP_CIPHER_AESCCM;
+
+ if (srev >= AR5K_SREV_AR2414) {
+ common->crypt_caps |= ATH_CRYPT_CAP_MIC_COMBINED;
+ AR5K_REG_ENABLE_BITS(ah, AR5K_MISC_MODE,
+ AR5K_MISC_MODE_COMBINED_MIC);
+ }
+
+ /* MAC address is cleared until add_interface */
+ ath5k_hw_set_lladdr(ah, zero_mac);
+
+ /* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */
+ memcpy(common->curbssid, ath_bcast_mac, ETH_ALEN);
+ ath5k_hw_set_bssid(ah);
+ ath5k_hw_set_opmode(ah, ah->opmode);
+
+ ath5k_hw_rfgain_opt_init(ah);
+
+ ath5k_hw_init_nfcal_hist(ah);
+
+ /* turn on HW LEDs */
+ ath5k_hw_set_ledstate(ah, AR5K_LED_INIT);
+
+ return 0;
+err:
+ return ret;
+}
+
+/**
+ * ath5k_hw_deinit - Free the ath5k_hw struct
+ *
+ * @ah: The &struct ath5k_hw
+ */
+void ath5k_hw_deinit(struct ath5k_hw *ah)
+{
+ __set_bit(ATH_STAT_INVALID, ah->status);
+
+ if (ah->ah_rf_banks != NULL)
+ kfree(ah->ah_rf_banks);
+
+ ath5k_eeprom_detach(ah);
+
+ /* assume interrupts are down */
+}
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
new file mode 100644
index 0000000..b346d04
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -0,0 +1,2952 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2005 Atheros Communications, Inc.
+ * Copyright (c) 2006 Devicescape Software, Inc.
+ * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
+ * Copyright (c) 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * 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 NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/hardirq.h>
+#include <linux/if.h>
+#include <linux/io.h>
+#include <linux/netdevice.h>
+#include <linux/cache.h>
+#include <linux/ethtool.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/etherdevice.h>
+#include <linux/nl80211.h>
+
+#include <net/ieee80211_radiotap.h>
+
+#include <asm/unaligned.h>
+
+#include "base.h"
+#include "reg.h"
+#include "debug.h"
+#include "ani.h"
+#include "ath5k.h"
+#include "../regd.h"
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+int ath5k_modparam_nohwcrypt;
+module_param_named(nohwcrypt, ath5k_modparam_nohwcrypt, bool, S_IRUGO);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
+
+static int modparam_all_channels;
+module_param_named(all_channels, modparam_all_channels, bool, S_IRUGO);
+MODULE_PARM_DESC(all_channels, "Expose all channels the device can use.");
+
+static int modparam_fastchanswitch;
+module_param_named(fastchanswitch, modparam_fastchanswitch, bool, S_IRUGO);
+MODULE_PARM_DESC(fastchanswitch, "Enable fast channel switching for AR2413/AR5413 radios.");
+
+
+/* Module info */
+MODULE_AUTHOR("Jiri Slaby");
+MODULE_AUTHOR("Nick Kossifidis");
+MODULE_DESCRIPTION("Support for 5xxx series of Atheros 802.11 wireless LAN cards.");
+MODULE_SUPPORTED_DEVICE("Atheros 5xxx WLAN cards");
+MODULE_LICENSE("Dual BSD/GPL");
+
+static int ath5k_init(struct ieee80211_hw *hw);
+static int ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,
+ bool skip_pcu);
+
+/* Known SREVs */
+static const struct ath5k_srev_name srev_names[] = {
+#ifdef CONFIG_ATHEROS_AR231X
+ { "5312", AR5K_VERSION_MAC, AR5K_SREV_AR5312_R2 },
+ { "5312", AR5K_VERSION_MAC, AR5K_SREV_AR5312_R7 },
+ { "2313", AR5K_VERSION_MAC, AR5K_SREV_AR2313_R8 },
+ { "2315", AR5K_VERSION_MAC, AR5K_SREV_AR2315_R6 },
+ { "2315", AR5K_VERSION_MAC, AR5K_SREV_AR2315_R7 },
+ { "2317", AR5K_VERSION_MAC, AR5K_SREV_AR2317_R1 },
+ { "2317", AR5K_VERSION_MAC, AR5K_SREV_AR2317_R2 },
+#else
+ { "5210", AR5K_VERSION_MAC, AR5K_SREV_AR5210 },
+ { "5311", AR5K_VERSION_MAC, AR5K_SREV_AR5311 },
+ { "5311A", AR5K_VERSION_MAC, AR5K_SREV_AR5311A },
+ { "5311B", AR5K_VERSION_MAC, AR5K_SREV_AR5311B },
+ { "5211", AR5K_VERSION_MAC, AR5K_SREV_AR5211 },
+ { "5212", AR5K_VERSION_MAC, AR5K_SREV_AR5212 },
+ { "5213", AR5K_VERSION_MAC, AR5K_SREV_AR5213 },
+ { "5213A", AR5K_VERSION_MAC, AR5K_SREV_AR5213A },
+ { "2413", AR5K_VERSION_MAC, AR5K_SREV_AR2413 },
+ { "2414", AR5K_VERSION_MAC, AR5K_SREV_AR2414 },
+ { "5424", AR5K_VERSION_MAC, AR5K_SREV_AR5424 },
+ { "5413", AR5K_VERSION_MAC, AR5K_SREV_AR5413 },
+ { "5414", AR5K_VERSION_MAC, AR5K_SREV_AR5414 },
+ { "2415", AR5K_VERSION_MAC, AR5K_SREV_AR2415 },
+ { "5416", AR5K_VERSION_MAC, AR5K_SREV_AR5416 },
+ { "5418", AR5K_VERSION_MAC, AR5K_SREV_AR5418 },
+ { "2425", AR5K_VERSION_MAC, AR5K_SREV_AR2425 },
+ { "2417", AR5K_VERSION_MAC, AR5K_SREV_AR2417 },
+#endif
+ { "xxxxx", AR5K_VERSION_MAC, AR5K_SREV_UNKNOWN },
+ { "5110", AR5K_VERSION_RAD, AR5K_SREV_RAD_5110 },
+ { "5111", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111 },
+ { "5111A", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111A },
+ { "2111", AR5K_VERSION_RAD, AR5K_SREV_RAD_2111 },
+ { "5112", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112 },
+ { "5112A", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112A },
+ { "5112B", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112B },
+ { "2112", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112 },
+ { "2112A", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112A },
+ { "2112B", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112B },
+ { "2413", AR5K_VERSION_RAD, AR5K_SREV_RAD_2413 },
+ { "5413", AR5K_VERSION_RAD, AR5K_SREV_RAD_5413 },
+ { "5424", AR5K_VERSION_RAD, AR5K_SREV_RAD_5424 },
+ { "5133", AR5K_VERSION_RAD, AR5K_SREV_RAD_5133 },
+#ifdef CONFIG_ATHEROS_AR231X
+ { "2316", AR5K_VERSION_RAD, AR5K_SREV_RAD_2316 },
+ { "2317", AR5K_VERSION_RAD, AR5K_SREV_RAD_2317 },
+#endif
+ { "xxxxx", AR5K_VERSION_RAD, AR5K_SREV_UNKNOWN },
+};
+
+static const struct ieee80211_rate ath5k_rates[] = {
+ { .bitrate = 10,
+ .hw_value = ATH5K_RATE_CODE_1M, },
+ { .bitrate = 20,
+ .hw_value = ATH5K_RATE_CODE_2M,
+ .hw_value_short = ATH5K_RATE_CODE_2M | AR5K_SET_SHORT_PREAMBLE,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 55,
+ .hw_value = ATH5K_RATE_CODE_5_5M,
+ .hw_value_short = ATH5K_RATE_CODE_5_5M | AR5K_SET_SHORT_PREAMBLE,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 110,
+ .hw_value = ATH5K_RATE_CODE_11M,
+ .hw_value_short = ATH5K_RATE_CODE_11M | AR5K_SET_SHORT_PREAMBLE,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 60,
+ .hw_value = ATH5K_RATE_CODE_6M,
+ .flags = 0 },
+ { .bitrate = 90,
+ .hw_value = ATH5K_RATE_CODE_9M,
+ .flags = 0 },
+ { .bitrate = 120,
+ .hw_value = ATH5K_RATE_CODE_12M,
+ .flags = 0 },
+ { .bitrate = 180,
+ .hw_value = ATH5K_RATE_CODE_18M,
+ .flags = 0 },
+ { .bitrate = 240,
+ .hw_value = ATH5K_RATE_CODE_24M,
+ .flags = 0 },
+ { .bitrate = 360,
+ .hw_value = ATH5K_RATE_CODE_36M,
+ .flags = 0 },
+ { .bitrate = 480,
+ .hw_value = ATH5K_RATE_CODE_48M,
+ .flags = 0 },
+ { .bitrate = 540,
+ .hw_value = ATH5K_RATE_CODE_54M,
+ .flags = 0 },
+ /* XR missing */
+};
+
+static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
+{
+ u64 tsf = ath5k_hw_get_tsf64(ah);
+
+ if ((tsf & 0x7fff) < rstamp)
+ tsf -= 0x8000;
+
+ return (tsf & ~0x7fff) | rstamp;
+}
+
+const char *
+ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val)
+{
+ const char *name = "xxxxx";
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(srev_names); i++) {
+ if (srev_names[i].sr_type != type)
+ continue;
+
+ if ((val & 0xf0) == srev_names[i].sr_val)
+ name = srev_names[i].sr_name;
+
+ if ((val & 0xff) == srev_names[i].sr_val) {
+ name = srev_names[i].sr_name;
+ break;
+ }
+ }
+
+ return name;
+}
+static unsigned int ath5k_ioread32(void *hw_priv, u32 reg_offset)
+{
+ struct ath5k_hw *ah = (struct ath5k_hw *) hw_priv;
+ return ath5k_hw_reg_read(ah, reg_offset);
+}
+
+static void ath5k_iowrite32(void *hw_priv, u32 val, u32 reg_offset)
+{
+ struct ath5k_hw *ah = (struct ath5k_hw *) hw_priv;
+ ath5k_hw_reg_write(ah, val, reg_offset);
+}
+
+static const struct ath_ops ath5k_common_ops = {
+ .read = ath5k_ioread32,
+ .write = ath5k_iowrite32,
+};
+
+/***********************\
+* Driver Initialization *
+\***********************/
+
+static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
+{
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct ath5k_hw *ah = hw->priv;
+ struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
+
+ return ath_reg_notifier_apply(wiphy, request, regulatory);
+}
+
+/********************\
+* Channel/mode setup *
+\********************/
+
+/*
+ * Returns true for the channel numbers used without all_channels modparam.
+ */
+static bool ath5k_is_standard_channel(short chan, enum ieee80211_band band)
+{
+ if (band == IEEE80211_BAND_2GHZ && chan <= 14)
+ return true;
+
+ return /* UNII 1,2 */
+ (((chan & 3) == 0 && chan >= 36 && chan <= 64) ||
+ /* midband */
+ ((chan & 3) == 0 && chan >= 100 && chan <= 140) ||
+ /* UNII-3 */
+ ((chan & 3) == 1 && chan >= 149 && chan <= 165) ||
+ /* 802.11j 5.030-5.080 GHz (20MHz) */
+ (chan == 8 || chan == 12 || chan == 16) ||
+ /* 802.11j 4.9GHz (20MHz) */
+ (chan == 184 || chan == 188 || chan == 192 || chan == 196));
+}
+
+static unsigned int
+ath5k_setup_channels(struct ath5k_hw *ah, struct ieee80211_channel *channels,
+ unsigned int mode, unsigned int max)
+{
+ unsigned int count, size, freq, ch;
+ enum ieee80211_band band;
+
+ switch (mode) {
+ case AR5K_MODE_11A:
+ /* 1..220, but 2GHz frequencies are filtered by check_channel */
+ size = 220;
+ band = IEEE80211_BAND_5GHZ;
+ break;
+ case AR5K_MODE_11B:
+ case AR5K_MODE_11G:
+ size = 26;
+ band = IEEE80211_BAND_2GHZ;
+ break;
+ default:
+ ATH5K_WARN(ah, "bad mode, not copying channels\n");
+ return 0;
+ }
+
+ count = 0;
+ for (ch = 1; ch <= size && count < max; ch++) {
+ freq = ieee80211_channel_to_frequency(ch, band);
+
+ if (freq == 0) /* mapping failed - not a standard channel */
+ continue;
+
+ /* Write channel info, needed for ath5k_channel_ok() */
+ channels[count].center_freq = freq;
+ channels[count].band = band;
+ channels[count].hw_value = mode;
+
+ /* Check if channel is supported by the chipset */
+ if (!ath5k_channel_ok(ah, &channels[count]))
+ continue;
+
+ if (!modparam_all_channels &&
+ !ath5k_is_standard_channel(ch, band))
+ continue;
+
+ count++;
+ }
+
+ return count;
+}
+
+static void
+ath5k_setup_rate_idx(struct ath5k_hw *ah, struct ieee80211_supported_band *b)
+{
+ u8 i;
+
+ for (i = 0; i < AR5K_MAX_RATES; i++)
+ ah->rate_idx[b->band][i] = -1;
+
+ for (i = 0; i < b->n_bitrates; i++) {
+ ah->rate_idx[b->band][b->bitrates[i].hw_value] = i;
+ if (b->bitrates[i].hw_value_short)
+ ah->rate_idx[b->band][b->bitrates[i].hw_value_short] = i;
+ }
+}
+
+static int
+ath5k_setup_bands(struct ieee80211_hw *hw)
+{
+ struct ath5k_hw *ah = hw->priv;
+ struct ieee80211_supported_band *sband;
+ int max_c, count_c = 0;
+ int i;
+
+ BUILD_BUG_ON(ARRAY_SIZE(ah->sbands) < IEEE80211_NUM_BANDS);
+ max_c = ARRAY_SIZE(ah->channels);
+
+ /* 2GHz band */
+ sband = &ah->sbands[IEEE80211_BAND_2GHZ];
+ sband->band = IEEE80211_BAND_2GHZ;
+ sband->bitrates = &ah->rates[IEEE80211_BAND_2GHZ][0];
+
+ if (test_bit(AR5K_MODE_11G, ah->ah_capabilities.cap_mode)) {
+ /* G mode */
+ memcpy(sband->bitrates, &ath5k_rates[0],
+ sizeof(struct ieee80211_rate) * 12);
+ sband->n_bitrates = 12;
+
+ sband->channels = ah->channels;
+ sband->n_channels = ath5k_setup_channels(ah, sband->channels,
+ AR5K_MODE_11G, max_c);
+
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband;
+ count_c = sband->n_channels;
+ max_c -= count_c;
+ } else if (test_bit(AR5K_MODE_11B, ah->ah_capabilities.cap_mode)) {
+ /* B mode */
+ memcpy(sband->bitrates, &ath5k_rates[0],
+ sizeof(struct ieee80211_rate) * 4);
+ sband->n_bitrates = 4;
+
+ /* 5211 only supports B rates and uses 4bit rate codes
+ * (e.g normally we have 0x1B for 1M, but on 5211 we have 0x0B)
+ * fix them up here:
+ */
+ if (ah->ah_version == AR5K_AR5211) {
+ for (i = 0; i < 4; i++) {
+ sband->bitrates[i].hw_value =
+ sband->bitrates[i].hw_value & 0xF;
+ sband->bitrates[i].hw_value_short =
+ sband->bitrates[i].hw_value_short & 0xF;
+ }
+ }
+
+ sband->channels = ah->channels;
+ sband->n_channels = ath5k_setup_channels(ah, sband->channels,
+ AR5K_MODE_11B, max_c);
+
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband;
+ count_c = sband->n_channels;
+ max_c -= count_c;
+ }
+ ath5k_setup_rate_idx(ah, sband);
+
+ /* 5GHz band, A mode */
+ if (test_bit(AR5K_MODE_11A, ah->ah_capabilities.cap_mode)) {
+ sband = &ah->sbands[IEEE80211_BAND_5GHZ];
+ sband->band = IEEE80211_BAND_5GHZ;
+ sband->bitrates = &ah->rates[IEEE80211_BAND_5GHZ][0];
+
+ memcpy(sband->bitrates, &ath5k_rates[4],
+ sizeof(struct ieee80211_rate) * 8);
+ sband->n_bitrates = 8;
+
+ sband->channels = &ah->channels[count_c];
+ sband->n_channels = ath5k_setup_channels(ah, sband->channels,
+ AR5K_MODE_11A, max_c);
+
+ hw->wiphy->bands[IEEE80211_BAND_5GHZ] = sband;
+ }
+ ath5k_setup_rate_idx(ah, sband);
+
+ ath5k_debug_dump_bands(ah);
+
+ return 0;
+}
+
+/*
+ * Set/change channels. We always reset the chip.
+ * To accomplish this we must first cleanup any pending DMA,
+ * then restart stuff after a la ath5k_init.
+ *
+ * Called with ah->lock.
+ */
+int
+ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan)
+{
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
+ "channel set, resetting (%u -> %u MHz)\n",
+ ah->curchan->center_freq, chan->center_freq);
+
+ /*
+ * To switch channels clear any pending DMA operations;
+ * wait long enough for the RX fifo to drain, reset the
+ * hardware at the new frequency, and then re-enable
+ * the relevant bits of the h/w.
+ */
+ return ath5k_reset(ah, chan, true);
+}
+
+void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct ath5k_vif_iter_data *iter_data = data;
+ int i;
+ struct ath5k_vif *avf = (void *)vif->drv_priv;
+
+ if (iter_data->hw_macaddr)
+ for (i = 0; i < ETH_ALEN; i++)
+ iter_data->mask[i] &=
+ ~(iter_data->hw_macaddr[i] ^ mac[i]);
+
+ if (!iter_data->found_active) {
+ iter_data->found_active = true;
+ memcpy(iter_data->active_mac, mac, ETH_ALEN);
+ }
+
+ if (iter_data->need_set_hw_addr && iter_data->hw_macaddr)
+ if (compare_ether_addr(iter_data->hw_macaddr, mac) == 0)
+ iter_data->need_set_hw_addr = false;
+
+ if (!iter_data->any_assoc) {
+ if (avf->assoc)
+ iter_data->any_assoc = true;
+ }
+
+ /* Calculate combined mode - when APs are active, operate in AP mode.
+ * Otherwise use the mode of the new interface. This can currently
+ * only deal with combinations of APs and STAs. Only one ad-hoc
+ * interfaces is allowed.
+ */
+ if (avf->opmode == NL80211_IFTYPE_AP)
+ iter_data->opmode = NL80211_IFTYPE_AP;
+ else {
+ if (avf->opmode == NL80211_IFTYPE_STATION)
+ iter_data->n_stas++;
+ if (iter_data->opmode == NL80211_IFTYPE_UNSPECIFIED)
+ iter_data->opmode = avf->opmode;
+ }
+}
+
+void
+ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah,
+ struct ieee80211_vif *vif)
+{
+ struct ath_common *common = ath5k_hw_common(ah);
+ struct ath5k_vif_iter_data iter_data;
+ u32 rfilt;
+
+ /*
+ * Use the hardware MAC address as reference, the hardware uses it
+ * together with the BSSID mask when matching addresses.
+ */
+ iter_data.hw_macaddr = common->macaddr;
+ memset(&iter_data.mask, 0xff, ETH_ALEN);
+ iter_data.found_active = false;
+ iter_data.need_set_hw_addr = true;
+ iter_data.opmode = NL80211_IFTYPE_UNSPECIFIED;
+ iter_data.n_stas = 0;
+
+ if (vif)
+ ath5k_vif_iter(&iter_data, vif->addr, vif);
+
+ /* Get list of all active MAC addresses */
+ ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter,
+ &iter_data);
+ memcpy(ah->bssidmask, iter_data.mask, ETH_ALEN);
+
+ ah->opmode = iter_data.opmode;
+ if (ah->opmode == NL80211_IFTYPE_UNSPECIFIED)
+ /* Nothing active, default to station mode */
+ ah->opmode = NL80211_IFTYPE_STATION;
+
+ ath5k_hw_set_opmode(ah, ah->opmode);
+ ATH5K_DBG(ah, ATH5K_DEBUG_MODE, "mode setup opmode %d (%s)\n",
+ ah->opmode, ath_opmode_to_string(ah->opmode));
+
+ if (iter_data.need_set_hw_addr && iter_data.found_active)
+ ath5k_hw_set_lladdr(ah, iter_data.active_mac);
+
+ if (ath5k_hw_hasbssidmask(ah))
+ ath5k_hw_set_bssid_mask(ah, ah->bssidmask);
+
+ /* Set up RX Filter */
+ if (iter_data.n_stas > 1) {
+ /* If you have multiple STA interfaces connected to
+ * different APs, ARPs are not received (most of the time?)
+ * Enabling PROMISC appears to fix that problem.
+ */
+ ah->filter_flags |= AR5K_RX_FILTER_PROM;
+ }
+
+ rfilt = ah->filter_flags;
+ ath5k_hw_set_rx_filter(ah, rfilt);
+ ATH5K_DBG(ah, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt);
+}
+
+static inline int
+ath5k_hw_to_driver_rix(struct ath5k_hw *ah, int hw_rix)
+{
+ int rix;
+
+ /* return base rate on errors */
+ if (WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES,
+ "hw_rix out of bounds: %x\n", hw_rix))
+ return 0;
+
+ rix = ah->rate_idx[ah->curchan->band][hw_rix];
+ if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix))
+ rix = 0;
+
+ return rix;
+}
+
+/***************\
+* Buffers setup *
+\***************/
+
+static
+struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_hw *ah, dma_addr_t *skb_addr)
+{
+ struct ath_common *common = ath5k_hw_common(ah);
+ struct sk_buff *skb;
+
+ /*
+ * Allocate buffer with headroom_needed space for the
+ * fake physical layer header at the start.
+ */
+ skb = ath_rxbuf_alloc(common,
+ common->rx_bufsize,
+ GFP_ATOMIC);
+
+ if (!skb) {
+ ATH5K_ERR(ah, "can't alloc skbuff of size %u\n",
+ common->rx_bufsize);
+ return NULL;
+ }
+
+ *skb_addr = dma_map_single(ah->dev,
+ skb->data, common->rx_bufsize,
+ DMA_FROM_DEVICE);
+
+ if (unlikely(dma_mapping_error(ah->dev, *skb_addr))) {
+ ATH5K_ERR(ah, "%s: DMA mapping failed\n", __func__);
+ dev_kfree_skb(skb);
+ return NULL;
+ }
+ return skb;
+}
+
+static int
+ath5k_rxbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf)
+{
+ struct sk_buff *skb = bf->skb;
+ struct ath5k_desc *ds;
+ int ret;
+
+ if (!skb) {
+ skb = ath5k_rx_skb_alloc(ah, &bf->skbaddr);
+ if (!skb)
+ return -ENOMEM;
+ bf->skb = skb;
+ }
+
+ /*
+ * Setup descriptors. For receive we always terminate
+ * the descriptor list with a self-linked entry so we'll
+ * not get overrun under high load (as can happen with a
+ * 5212 when ANI processing enables PHY error frames).
+ *
+ * To ensure the last descriptor is self-linked we create
+ * each descriptor as self-linked and add it to the end. As
+ * each additional descriptor is added the previous self-linked
+ * entry is "fixed" naturally. This should be safe even
+ * if DMA is happening. When processing RX interrupts we
+ * never remove/process the last, self-linked, entry on the
+ * descriptor list. This ensures the hardware always has
+ * someplace to write a new frame.
+ */
+ ds = bf->desc;
+ ds->ds_link = bf->daddr; /* link to self */
+ ds->ds_data = bf->skbaddr;
+ ret = ath5k_hw_setup_rx_desc(ah, ds, ah->common.rx_bufsize, 0);
+ if (ret) {
+ ATH5K_ERR(ah, "%s: could not setup RX desc\n", __func__);
+ return ret;
+ }
+
+ if (ah->rxlink != NULL)
+ *ah->rxlink = bf->daddr;
+ ah->rxlink = &ds->ds_link;
+ return 0;
+}
+
+static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr;
+ enum ath5k_pkt_type htype;
+ __le16 fc;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ fc = hdr->frame_control;
+
+ if (ieee80211_is_beacon(fc))
+ htype = AR5K_PKT_TYPE_BEACON;
+ else if (ieee80211_is_probe_resp(fc))
+ htype = AR5K_PKT_TYPE_PROBE_RESP;
+ else if (ieee80211_is_atim(fc))
+ htype = AR5K_PKT_TYPE_ATIM;
+ else if (ieee80211_is_pspoll(fc))
+ htype = AR5K_PKT_TYPE_PSPOLL;
+ else
+ htype = AR5K_PKT_TYPE_NORMAL;
+
+ return htype;
+}
+
+static int
+ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
+ struct ath5k_txq *txq, int padsize)
+{
+ struct ath5k_desc *ds = bf->desc;
+ struct sk_buff *skb = bf->skb;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID;
+ struct ieee80211_rate *rate;
+ unsigned int mrr_rate[3], mrr_tries[3];
+ int i, ret;
+ u16 hw_rate;
+ u16 cts_rate = 0;
+ u16 duration = 0;
+ u8 rc_flags;
+
+ flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK;
+
+ /* XXX endianness */
+ bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len,
+ DMA_TO_DEVICE);
+
+ rate = ieee80211_get_tx_rate(ah->hw, info);
+ if (!rate) {
+ ret = -EINVAL;
+ goto err_unmap;
+ }
+
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ flags |= AR5K_TXDESC_NOACK;
+
+ rc_flags = info->control.rates[0].flags;
+ hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ?
+ rate->hw_value_short : rate->hw_value;
+
+ pktlen = skb->len;
+
+ /* FIXME: If we are in g mode and rate is a CCK rate
+ * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta
+ * from tx power (value is in dB units already) */
+ if (info->control.hw_key) {
+ keyidx = info->control.hw_key->hw_key_idx;
+ pktlen += info->control.hw_key->icv_len;
+ }
+ if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
+ flags |= AR5K_TXDESC_RTSENA;
+ cts_rate = ieee80211_get_rts_cts_rate(ah->hw, info)->hw_value;
+ duration = le16_to_cpu(ieee80211_rts_duration(ah->hw,
+ info->control.vif, pktlen, info));
+ }
+ if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
+ flags |= AR5K_TXDESC_CTSENA;
+ cts_rate = ieee80211_get_rts_cts_rate(ah->hw, info)->hw_value;
+ duration = le16_to_cpu(ieee80211_ctstoself_duration(ah->hw,
+ info->control.vif, pktlen, info));
+ }
+ ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
+ ieee80211_get_hdrlen_from_skb(skb), padsize,
+ get_hw_packet_type(skb),
+ (ah->power_level * 2),
+ hw_rate,
+ info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags,
+ cts_rate, duration);
+ if (ret)
+ goto err_unmap;
+
+ memset(mrr_rate, 0, sizeof(mrr_rate));
+ memset(mrr_tries, 0, sizeof(mrr_tries));
+ for (i = 0; i < 3; i++) {
+ rate = ieee80211_get_alt_retry_rate(ah->hw, info, i);
+ if (!rate)
+ break;
+
+ mrr_rate[i] = rate->hw_value;
+ mrr_tries[i] = info->control.rates[i + 1].count;
+ }
+
+ ath5k_hw_setup_mrr_tx_desc(ah, ds,
+ mrr_rate[0], mrr_tries[0],
+ mrr_rate[1], mrr_tries[1],
+ mrr_rate[2], mrr_tries[2]);
+
+ ds->ds_link = 0;
+ ds->ds_data = bf->skbaddr;
+
+ spin_lock_bh(&txq->lock);
+ list_add_tail(&bf->list, &txq->q);
+ txq->txq_len++;
+ if (txq->link == NULL) /* is this first packet? */
+ ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr);
+ else /* no, so only link it */
+ *txq->link = bf->daddr;
+
+ txq->link = &ds->ds_link;
+ ath5k_hw_start_tx_dma(ah, txq->qnum);
+ mmiowb();
+ spin_unlock_bh(&txq->lock);
+
+ return 0;
+err_unmap:
+ dma_unmap_single(ah->dev, bf->skbaddr, skb->len, DMA_TO_DEVICE);
+ return ret;
+}
+
+/*******************\
+* Descriptors setup *
+\*******************/
+
+static int
+ath5k_desc_alloc(struct ath5k_hw *ah)
+{
+ struct ath5k_desc *ds;
+ struct ath5k_buf *bf;
+ dma_addr_t da;
+ unsigned int i;
+ int ret;
+
+ /* allocate descriptors */
+ ah->desc_len = sizeof(struct ath5k_desc) *
+ (ATH_TXBUF + ATH_RXBUF + ATH_BCBUF + 1);
+
+ ah->desc = dma_alloc_coherent(ah->dev, ah->desc_len,
+ &ah->desc_daddr, GFP_KERNEL);
+ if (ah->desc == NULL) {
+ ATH5K_ERR(ah, "can't allocate descriptors\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ ds = ah->desc;
+ da = ah->desc_daddr;
+ ATH5K_DBG(ah, ATH5K_DEBUG_ANY, "DMA map: %p (%zu) -> %llx\n",
+ ds, ah->desc_len, (unsigned long long)ah->desc_daddr);
+
+ bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF,
+ sizeof(struct ath5k_buf), GFP_KERNEL);
+ if (bf == NULL) {
+ ATH5K_ERR(ah, "can't allocate bufptr\n");
+ ret = -ENOMEM;
+ goto err_free;
+ }
+ ah->bufptr = bf;
+
+ INIT_LIST_HEAD(&ah->rxbuf);
+ for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) {
+ bf->desc = ds;
+ bf->daddr = da;
+ list_add_tail(&bf->list, &ah->rxbuf);
+ }
+
+ INIT_LIST_HEAD(&ah->txbuf);
+ ah->txbuf_len = ATH_TXBUF;
+ for (i = 0; i < ATH_TXBUF; i++, bf++, ds++, da += sizeof(*ds)) {
+ bf->desc = ds;
+ bf->daddr = da;
+ list_add_tail(&bf->list, &ah->txbuf);
+ }
+
+ /* beacon buffers */
+ INIT_LIST_HEAD(&ah->bcbuf);
+ for (i = 0; i < ATH_BCBUF; i++, bf++, ds++, da += sizeof(*ds)) {
+ bf->desc = ds;
+ bf->daddr = da;
+ list_add_tail(&bf->list, &ah->bcbuf);
+ }
+
+ return 0;
+err_free:
+ dma_free_coherent(ah->dev, ah->desc_len, ah->desc, ah->desc_daddr);
+err:
+ ah->desc = NULL;
+ return ret;
+}
+
+void
+ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf)
+{
+ BUG_ON(!bf);
+ if (!bf->skb)
+ return;
+ dma_unmap_single(ah->dev, bf->skbaddr, bf->skb->len,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_any(bf->skb);
+ bf->skb = NULL;
+ bf->skbaddr = 0;
+ bf->desc->ds_data = 0;
+}
+
+void
+ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf)
+{
+ struct ath_common *common = ath5k_hw_common(ah);
+
+ BUG_ON(!bf);
+ if (!bf->skb)
+ return;
+ dma_unmap_single(ah->dev, bf->skbaddr, common->rx_bufsize,
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_any(bf->skb);
+ bf->skb = NULL;
+ bf->skbaddr = 0;
+ bf->desc->ds_data = 0;
+}
+
+static void
+ath5k_desc_free(struct ath5k_hw *ah)
+{
+ struct ath5k_buf *bf;
+
+ list_for_each_entry(bf, &ah->txbuf, list)
+ ath5k_txbuf_free_skb(ah, bf);
+ list_for_each_entry(bf, &ah->rxbuf, list)
+ ath5k_rxbuf_free_skb(ah, bf);
+ list_for_each_entry(bf, &ah->bcbuf, list)
+ ath5k_txbuf_free_skb(ah, bf);
+
+ /* Free memory associated with all descriptors */
+ dma_free_coherent(ah->dev, ah->desc_len, ah->desc, ah->desc_daddr);
+ ah->desc = NULL;
+ ah->desc_daddr = 0;
+
+ kfree(ah->bufptr);
+ ah->bufptr = NULL;
+}
+
+
+/**************\
+* Queues setup *
+\**************/
+
+static struct ath5k_txq *
+ath5k_txq_setup(struct ath5k_hw *ah,
+ int qtype, int subtype)
+{
+ struct ath5k_txq *txq;
+ struct ath5k_txq_info qi = {
+ .tqi_subtype = subtype,
+ /* XXX: default values not correct for B and XR channels,
+ * but who cares? */
+ .tqi_aifs = AR5K_TUNE_AIFS,
+ .tqi_cw_min = AR5K_TUNE_CWMIN,
+ .tqi_cw_max = AR5K_TUNE_CWMAX
+ };
+ int qnum;
+
+ /*
+ * Enable interrupts only for EOL and DESC conditions.
+ * We mark tx descriptors to receive a DESC interrupt
+ * when a tx queue gets deep; otherwise we wait for the
+ * EOL to reap descriptors. Note that this is done to
+ * reduce interrupt load and this only defers reaping
+ * descriptors, never transmitting frames. Aside from
+ * reducing interrupts this also permits more concurrency.
+ * The only potential downside is if the tx queue backs
+ * up in which case the top half of the kernel may backup
+ * due to a lack of tx descriptors.
+ */
+ qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE |
+ AR5K_TXQ_FLAG_TXDESCINT_ENABLE;
+ qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi);
+ if (qnum < 0) {
+ /*
+ * NB: don't print a message, this happens
+ * normally on parts with too few tx queues
+ */
+ return ERR_PTR(qnum);
+ }
+ txq = &ah->txqs[qnum];
+ if (!txq->setup) {
+ txq->qnum = qnum;
+ txq->link = NULL;
+ INIT_LIST_HEAD(&txq->q);
+ spin_lock_init(&txq->lock);
+ txq->setup = true;
+ txq->txq_len = 0;
+ txq->txq_max = ATH5K_TXQ_LEN_MAX;
+ txq->txq_poll_mark = false;
+ txq->txq_stuck = 0;
+ }
+ return &ah->txqs[qnum];
+}
+
+static int
+ath5k_beaconq_setup(struct ath5k_hw *ah)
+{
+ struct ath5k_txq_info qi = {
+ /* XXX: default values not correct for B and XR channels,
+ * but who cares? */
+ .tqi_aifs = AR5K_TUNE_AIFS,
+ .tqi_cw_min = AR5K_TUNE_CWMIN,
+ .tqi_cw_max = AR5K_TUNE_CWMAX,
+ /* NB: for dynamic turbo, don't enable any other interrupts */
+ .tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE
+ };
+
+ return ath5k_hw_setup_tx_queue(ah, AR5K_TX_QUEUE_BEACON, &qi);
+}
+
+static int
+ath5k_beaconq_config(struct ath5k_hw *ah)
+{
+ struct ath5k_txq_info qi;
+ int ret;
+
+ ret = ath5k_hw_get_tx_queueprops(ah, ah->bhalq, &qi);
+ if (ret)
+ goto err;
+
+ if (ah->opmode == NL80211_IFTYPE_AP ||
+ ah->opmode == NL80211_IFTYPE_MESH_POINT) {
+ /*
+ * Always burst out beacon and CAB traffic
+ * (aifs = cwmin = cwmax = 0)
+ */
+ qi.tqi_aifs = 0;
+ qi.tqi_cw_min = 0;
+ qi.tqi_cw_max = 0;
+ } else if (ah->opmode == NL80211_IFTYPE_ADHOC) {
+ /*
+ * Adhoc mode; backoff between 0 and (2 * cw_min).
+ */
+ qi.tqi_aifs = 0;
+ qi.tqi_cw_min = 0;
+ qi.tqi_cw_max = 2 * AR5K_TUNE_CWMIN;
+ }
+
+ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON,
+ "beacon queueprops tqi_aifs:%d tqi_cw_min:%d tqi_cw_max:%d\n",
+ qi.tqi_aifs, qi.tqi_cw_min, qi.tqi_cw_max);
+
+ ret = ath5k_hw_set_tx_queueprops(ah, ah->bhalq, &qi);
+ if (ret) {
+ ATH5K_ERR(ah, "%s: unable to update parameters for beacon "
+ "hardware queue!\n", __func__);
+ goto err;
+ }
+ ret = ath5k_hw_reset_tx_queue(ah, ah->bhalq); /* push to h/w */
+ if (ret)
+ goto err;
+
+ /* reconfigure cabq with ready time to 80% of beacon_interval */
+ ret = ath5k_hw_get_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
+ if (ret)
+ goto err;
+
+ qi.tqi_ready_time = (ah->bintval * 80) / 100;
+ ret = ath5k_hw_set_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
+ if (ret)
+ goto err;
+
+ ret = ath5k_hw_reset_tx_queue(ah, AR5K_TX_QUEUE_ID_CAB);
+err:
+ return ret;
+}
+
+/**
+ * ath5k_drain_tx_buffs - Empty tx buffers
+ *
+ * @ah The &struct ath5k_hw
+ *
+ * Empty tx buffers from all queues in preparation
+ * of a reset or during shutdown.
+ *
+ * NB: this assumes output has been stopped and
+ * we do not need to block ath5k_tx_tasklet
+ */
+static void
+ath5k_drain_tx_buffs(struct ath5k_hw *ah)
+{
+ struct ath5k_txq *txq;
+ struct ath5k_buf *bf, *bf0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ah->txqs); i++) {
+ if (ah->txqs[i].setup) {
+ txq = &ah->txqs[i];
+ spin_lock_bh(&txq->lock);
+ list_for_each_entry_safe(bf, bf0, &txq->q, list) {
+ ath5k_debug_printtxbuf(ah, bf);
+
+ ath5k_txbuf_free_skb(ah, bf);
+
+ spin_lock_bh(&ah->txbuflock);
+ list_move_tail(&bf->list, &ah->txbuf);
+ ah->txbuf_len++;
+ txq->txq_len--;
+ spin_unlock_bh(&ah->txbuflock);
+ }
+ txq->link = NULL;
+ txq->txq_poll_mark = false;
+ spin_unlock_bh(&txq->lock);
+ }
+ }
+}
+
+static void
+ath5k_txq_release(struct ath5k_hw *ah)
+{
+ struct ath5k_txq *txq = ah->txqs;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ah->txqs); i++, txq++)
+ if (txq->setup) {
+ ath5k_hw_release_tx_queue(ah, txq->qnum);
+ txq->setup = false;
+ }
+}
+
+
+/*************\
+* RX Handling *
+\*************/
+
+/*
+ * Enable the receive h/w following a reset.
+ */
+static int
+ath5k_rx_start(struct ath5k_hw *ah)
+{
+ struct ath_common *common = ath5k_hw_common(ah);
+ struct ath5k_buf *bf;
+ int ret;
+
+ common->rx_bufsize = roundup(IEEE80211_MAX_FRAME_LEN, common->cachelsz);
+
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n",
+ common->cachelsz, common->rx_bufsize);
+
+ spin_lock_bh(&ah->rxbuflock);
+ ah->rxlink = NULL;
+ list_for_each_entry(bf, &ah->rxbuf, list) {
+ ret = ath5k_rxbuf_setup(ah, bf);
+ if (ret != 0) {
+ spin_unlock_bh(&ah->rxbuflock);
+ goto err;
+ }
+ }
+ bf = list_first_entry(&ah->rxbuf, struct ath5k_buf, list);
+ ath5k_hw_set_rxdp(ah, bf->daddr);
+ spin_unlock_bh(&ah->rxbuflock);
+
+ ath5k_hw_start_rx_dma(ah); /* enable recv descriptors */
+ ath5k_update_bssid_mask_and_opmode(ah, NULL); /* set filters, etc. */
+ ath5k_hw_start_rx_pcu(ah); /* re-enable PCU/DMA engine */
+
+ return 0;
+err:
+ return ret;
+}
+
+/*
+ * Disable the receive logic on PCU (DRU)
+ * In preparation for a shutdown.
+ *
+ * Note: Doesn't stop rx DMA, ath5k_hw_dma_stop
+ * does.
+ */
+static void
+ath5k_rx_stop(struct ath5k_hw *ah)
+{
+
+ ath5k_hw_set_rx_filter(ah, 0); /* clear recv filter */
+ ath5k_hw_stop_rx_pcu(ah); /* disable PCU */
+
+ ath5k_debug_printrxbuffs(ah);
+}
+
+static unsigned int
+ath5k_rx_decrypted(struct ath5k_hw *ah, struct sk_buff *skb,
+ struct ath5k_rx_status *rs)
+{
+ struct ath_common *common = ath5k_hw_common(ah);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ unsigned int keyix, hlen;
+
+ if (!(rs->rs_status & AR5K_RXERR_DECRYPT) &&
+ rs->rs_keyix != AR5K_RXKEYIX_INVALID)
+ return RX_FLAG_DECRYPTED;
+
+ /* Apparently when a default key is used to decrypt the packet
+ the hw does not set the index used to decrypt. In such cases
+ get the index from the packet. */
+ hlen = ieee80211_hdrlen(hdr->frame_control);
+ if (ieee80211_has_protected(hdr->frame_control) &&
+ !(rs->rs_status & AR5K_RXERR_DECRYPT) &&
+ skb->len >= hlen + 4) {
+ keyix = skb->data[hlen + 3] >> 6;
+
+ if (test_bit(keyix, common->keymap))
+ return RX_FLAG_DECRYPTED;
+ }
+
+ return 0;
+}
+
+
+static void
+ath5k_check_ibss_tsf(struct ath5k_hw *ah, struct sk_buff *skb,
+ struct ieee80211_rx_status *rxs)
+{
+ struct ath_common *common = ath5k_hw_common(ah);
+ u64 tsf, bc_tstamp;
+ u32 hw_tu;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+
+ if (ieee80211_is_beacon(mgmt->frame_control) &&
+ le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS &&
+ memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) == 0) {
+ /*
+ * Received an IBSS beacon with the same BSSID. Hardware *must*
+ * have updated the local TSF. We have to work around various
+ * hardware bugs, though...
+ */
+ tsf = ath5k_hw_get_tsf64(ah);
+ bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp);
+ hw_tu = TSF_TO_TU(tsf);
+
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON,
+ "beacon %llx mactime %llx (diff %lld) tsf now %llx\n",
+ (unsigned long long)bc_tstamp,
+ (unsigned long long)rxs->mactime,
+ (unsigned long long)(rxs->mactime - bc_tstamp),
+ (unsigned long long)tsf);
+
+ /*
+ * Sometimes the HW will give us a wrong tstamp in the rx
+ * status, causing the timestamp extension to go wrong.
+ * (This seems to happen especially with beacon frames bigger
+ * than 78 byte (incl. FCS))
+ * But we know that the receive timestamp must be later than the
+ * timestamp of the beacon since HW must have synced to that.
+ *
+ * NOTE: here we assume mactime to be after the frame was
+ * received, not like mac80211 which defines it at the start.
+ */
+ if (bc_tstamp > rxs->mactime) {
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON,
+ "fixing mactime from %llx to %llx\n",
+ (unsigned long long)rxs->mactime,
+ (unsigned long long)tsf);
+ rxs->mactime = tsf;
+ }
+
+ /*
+ * Local TSF might have moved higher than our beacon timers,
+ * in that case we have to update them to continue sending
+ * beacons. This also takes care of synchronizing beacon sending
+ * times with other stations.
+ */
+ if (hw_tu >= ah->nexttbtt)
+ ath5k_beacon_update_timers(ah, bc_tstamp);
+
+ /* Check if the beacon timers are still correct, because a TSF
+ * update might have created a window between them - for a
+ * longer description see the comment of this function: */
+ if (!ath5k_hw_check_beacon_timers(ah, ah->bintval)) {
+ ath5k_beacon_update_timers(ah, bc_tstamp);
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON,
+ "fixed beacon timers after beacon receive\n");
+ }
+ }
+}
+
+static void
+ath5k_update_beacon_rssi(struct ath5k_hw *ah, struct sk_buff *skb, int rssi)
+{
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+ struct ath_common *common = ath5k_hw_common(ah);
+
+ /* only beacons from our BSSID */
+ if (!ieee80211_is_beacon(mgmt->frame_control) ||
+ memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) != 0)
+ return;
+
+ ewma_add(&ah->ah_beacon_rssi_avg, rssi);
+
+ /* in IBSS mode we should keep RSSI statistics per neighbour */
+ /* le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS */
+}
+
+/*
+ * Compute padding position. skb must contain an IEEE 802.11 frame
+ */
+static int ath5k_common_padpos(struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ __le16 frame_control = hdr->frame_control;
+ int padpos = 24;
+
+ if (ieee80211_has_a4(frame_control))
+ padpos += ETH_ALEN;
+
+ if (ieee80211_is_data_qos(frame_control))
+ padpos += IEEE80211_QOS_CTL_LEN;
+
+ return padpos;
+}
+
+/*
+ * This function expects an 802.11 frame and returns the number of
+ * bytes added, or -1 if we don't have enough header room.
+ */
+static int ath5k_add_padding(struct sk_buff *skb)
+{
+ int padpos = ath5k_common_padpos(skb);
+ int padsize = padpos & 3;
+
+ if (padsize && skb->len > padpos) {
+
+ if (skb_headroom(skb) < padsize)
+ return -1;
+
+ skb_push(skb, padsize);
+ memmove(skb->data, skb->data + padsize, padpos);
+ return padsize;
+ }
+
+ return 0;
+}
+
+/*
+ * The MAC header is padded to have 32-bit boundary if the
+ * packet payload is non-zero. The general calculation for
+ * padsize would take into account odd header lengths:
+ * padsize = 4 - (hdrlen & 3); however, since only
+ * even-length headers are used, padding can only be 0 or 2
+ * bytes and we can optimize this a bit. We must not try to
+ * remove padding from short control frames that do not have a
+ * payload.
+ *
+ * This function expects an 802.11 frame and returns the number of
+ * bytes removed.
+ */
+static int ath5k_remove_padding(struct sk_buff *skb)
+{
+ int padpos = ath5k_common_padpos(skb);
+ int padsize = padpos & 3;
+
+ if (padsize && skb->len >= padpos + padsize) {
+ memmove(skb->data + padsize, skb->data, padpos);
+ skb_pull(skb, padsize);
+ return padsize;
+ }
+
+ return 0;
+}
+
+static void
+ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb,
+ struct ath5k_rx_status *rs)
+{
+ struct ieee80211_rx_status *rxs;
+
+ ath5k_remove_padding(skb);
+
+ rxs = IEEE80211_SKB_RXCB(skb);
+
+ rxs->flag = 0;
+ if (unlikely(rs->rs_status & AR5K_RXERR_MIC))
+ rxs->flag |= RX_FLAG_MMIC_ERROR;
+
+ /*
+ * always extend the mac timestamp, since this information is
+ * also needed for proper IBSS merging.
+ *
+ * XXX: it might be too late to do it here, since rs_tstamp is
+ * 15bit only. that means TSF extension has to be done within
+ * 32768usec (about 32ms). it might be necessary to move this to
+ * the interrupt handler, like it is done in madwifi.
+ *
+ * Unfortunately we don't know when the hardware takes the rx
+ * timestamp (beginning of phy frame, data frame, end of rx?).
+ * The only thing we know is that it is hardware specific...
+ * On AR5213 it seems the rx timestamp is at the end of the
+ * frame, but I'm not sure.
+ *
+ * NOTE: mac80211 defines mactime at the beginning of the first
+ * data symbol. Since we don't have any time references it's
+ * impossible to comply to that. This affects IBSS merge only
+ * right now, so it's not too bad...
+ */
+ rxs->mactime = ath5k_extend_tsf(ah, rs->rs_tstamp);
+ rxs->flag |= RX_FLAG_MACTIME_MPDU;
+
+ rxs->freq = ah->curchan->center_freq;
+ rxs->band = ah->curchan->band;
+
+ rxs->signal = ah->ah_noise_floor + rs->rs_rssi;
+
+ rxs->antenna = rs->rs_antenna;
+
+ if (rs->rs_antenna > 0 && rs->rs_antenna < 5)
+ ah->stats.antenna_rx[rs->rs_antenna]++;
+ else
+ ah->stats.antenna_rx[0]++; /* invalid */
+
+ rxs->rate_idx = ath5k_hw_to_driver_rix(ah, rs->rs_rate);
+ rxs->flag |= ath5k_rx_decrypted(ah, skb, rs);
+
+ if (rxs->rate_idx >= 0 && rs->rs_rate ==
+ ah->sbands[ah->curchan->band].bitrates[rxs->rate_idx].hw_value_short)
+ rxs->flag |= RX_FLAG_SHORTPRE;
+
+ trace_ath5k_rx(ah, skb);
+
+ ath5k_update_beacon_rssi(ah, skb, rs->rs_rssi);
+
+ /* check beacons in IBSS mode */
+ if (ah->opmode == NL80211_IFTYPE_ADHOC)
+ ath5k_check_ibss_tsf(ah, skb, rxs);
+
+ ieee80211_rx(ah->hw, skb);
+}
+
+/** ath5k_frame_receive_ok() - Do we want to receive this frame or not?
+ *
+ * Check if we want to further process this frame or not. Also update
+ * statistics. Return true if we want this frame, false if not.
+ */
+static bool
+ath5k_receive_frame_ok(struct ath5k_hw *ah, struct ath5k_rx_status *rs)
+{
+ ah->stats.rx_all_count++;
+ ah->stats.rx_bytes_count += rs->rs_datalen;
+
+ if (unlikely(rs->rs_status)) {
+ if (rs->rs_status & AR5K_RXERR_CRC)
+ ah->stats.rxerr_crc++;
+ if (rs->rs_status & AR5K_RXERR_FIFO)
+ ah->stats.rxerr_fifo++;
+ if (rs->rs_status & AR5K_RXERR_PHY) {
+ ah->stats.rxerr_phy++;
+ if (rs->rs_phyerr > 0 && rs->rs_phyerr < 32)
+ ah->stats.rxerr_phy_code[rs->rs_phyerr]++;
+ return false;
+ }
+ if (rs->rs_status & AR5K_RXERR_DECRYPT) {
+ /*
+ * Decrypt error. If the error occurred
+ * because there was no hardware key, then
+ * let the frame through so the upper layers
+ * can process it. This is necessary for 5210
+ * parts which have no way to setup a ``clear''
+ * key cache entry.
+ *
+ * XXX do key cache faulting
+ */
+ ah->stats.rxerr_decrypt++;
+ if (rs->rs_keyix == AR5K_RXKEYIX_INVALID &&
+ !(rs->rs_status & AR5K_RXERR_CRC))
+ return true;
+ }
+ if (rs->rs_status & AR5K_RXERR_MIC) {
+ ah->stats.rxerr_mic++;
+ return true;
+ }
+
+ /* reject any frames with non-crypto errors */
+ if (rs->rs_status & ~(AR5K_RXERR_DECRYPT))
+ return false;
+ }
+
+ if (unlikely(rs->rs_more)) {
+ ah->stats.rxerr_jumbo++;
+ return false;
+ }
+ return true;
+}
+
+static void
+ath5k_set_current_imask(struct ath5k_hw *ah)
+{
+ enum ath5k_int imask;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ah->irqlock, flags);
+ imask = ah->imask;
+ if (ah->rx_pending)
+ imask &= ~AR5K_INT_RX_ALL;
+ if (ah->tx_pending)
+ imask &= ~AR5K_INT_TX_ALL;
+ ath5k_hw_set_imr(ah, imask);
+ spin_unlock_irqrestore(&ah->irqlock, flags);
+}
+
+static void
+ath5k_tasklet_rx(unsigned long data)
+{
+ struct ath5k_rx_status rs = {};
+ struct sk_buff *skb, *next_skb;
+ dma_addr_t next_skb_addr;
+ struct ath5k_hw *ah = (void *)data;
+ struct ath_common *common = ath5k_hw_common(ah);
+ struct ath5k_buf *bf;
+ struct ath5k_desc *ds;
+ int ret;
+
+ spin_lock(&ah->rxbuflock);
+ if (list_empty(&ah->rxbuf)) {
+ ATH5K_WARN(ah, "empty rx buf pool\n");
+ goto unlock;
+ }
+ do {
+ bf = list_first_entry(&ah->rxbuf, struct ath5k_buf, list);
+ BUG_ON(bf->skb == NULL);
+ skb = bf->skb;
+ ds = bf->desc;
+
+ /* bail if HW is still using self-linked descriptor */
+ if (ath5k_hw_get_rxdp(ah) == bf->daddr)
+ break;
+
+ ret = ah->ah_proc_rx_desc(ah, ds, &rs);
+ if (unlikely(ret == -EINPROGRESS))
+ break;
+ else if (unlikely(ret)) {
+ ATH5K_ERR(ah, "error in processing rx descriptor\n");
+ ah->stats.rxerr_proc++;
+ break;
+ }
+
+ if (ath5k_receive_frame_ok(ah, &rs)) {
+ next_skb = ath5k_rx_skb_alloc(ah, &next_skb_addr);
+
+ /*
+ * If we can't replace bf->skb with a new skb under
+ * memory pressure, just skip this packet
+ */
+ if (!next_skb)
+ goto next;
+
+ dma_unmap_single(ah->dev, bf->skbaddr,
+ common->rx_bufsize,
+ DMA_FROM_DEVICE);
+
+ skb_put(skb, rs.rs_datalen);
+
+ ath5k_receive_frame(ah, skb, &rs);
+
+ bf->skb = next_skb;
+ bf->skbaddr = next_skb_addr;
+ }
+next:
+ list_move_tail(&bf->list, &ah->rxbuf);
+ } while (ath5k_rxbuf_setup(ah, bf) == 0);
+unlock:
+ spin_unlock(&ah->rxbuflock);
+ ah->rx_pending = false;
+ ath5k_set_current_imask(ah);
+}
+
+
+/*************\
+* TX Handling *
+\*************/
+
+void
+ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
+ struct ath5k_txq *txq)
+{
+ struct ath5k_hw *ah = hw->priv;
+ struct ath5k_buf *bf;
+ unsigned long flags;
+ int padsize;
+
+ trace_ath5k_tx(ah, skb, txq);
+
+ /*
+ * The hardware expects the header padded to 4 byte boundaries.
+ * If this is not the case, we add the padding after the header.
+ */
+ padsize = ath5k_add_padding(skb);
+ if (padsize < 0) {
+ ATH5K_ERR(ah, "tx hdrlen not %%4: not enough"
+ " headroom to pad");
+ goto drop_packet;
+ }
+
+ if (txq->txq_len >= txq->txq_max &&
+ txq->qnum <= AR5K_TX_QUEUE_ID_DATA_MAX)
+ ieee80211_stop_queue(hw, txq->qnum);
+
+ spin_lock_irqsave(&ah->txbuflock, flags);
+ if (list_empty(&ah->txbuf)) {
+ ATH5K_ERR(ah, "no further txbuf available, dropping packet\n");
+ spin_unlock_irqrestore(&ah->txbuflock, flags);
+ ieee80211_stop_queues(hw);
+ goto drop_packet;
+ }
+ bf = list_first_entry(&ah->txbuf, struct ath5k_buf, list);
+ list_del(&bf->list);
+ ah->txbuf_len--;
+ if (list_empty(&ah->txbuf))
+ ieee80211_stop_queues(hw);
+ spin_unlock_irqrestore(&ah->txbuflock, flags);
+
+ bf->skb = skb;
+
+ if (ath5k_txbuf_setup(ah, bf, txq, padsize)) {
+ bf->skb = NULL;
+ spin_lock_irqsave(&ah->txbuflock, flags);
+ list_add_tail(&bf->list, &ah->txbuf);
+ ah->txbuf_len++;
+ spin_unlock_irqrestore(&ah->txbuflock, flags);
+ goto drop_packet;
+ }
+ return;
+
+drop_packet:
+ dev_kfree_skb_any(skb);
+}
+
+static void
+ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb,
+ struct ath5k_txq *txq, struct ath5k_tx_status *ts)
+{
+ struct ieee80211_tx_info *info;
+ u8 tries[3];
+ int i;
+
+ ah->stats.tx_all_count++;
+ ah->stats.tx_bytes_count += skb->len;
+ info = IEEE80211_SKB_CB(skb);
+
+ tries[0] = info->status.rates[0].count;
+ tries[1] = info->status.rates[1].count;
+ tries[2] = info->status.rates[2].count;
+
+ ieee80211_tx_info_clear_status(info);
+
+ for (i = 0; i < ts->ts_final_idx; i++) {
+ struct ieee80211_tx_rate *r =
+ &info->status.rates[i];
+
+ r->count = tries[i];
+ }
+
+ info->status.rates[ts->ts_final_idx].count = ts->ts_final_retry;
+ info->status.rates[ts->ts_final_idx + 1].idx = -1;
+
+ if (unlikely(ts->ts_status)) {
+ ah->stats.ack_fail++;
+ if (ts->ts_status & AR5K_TXERR_FILT) {
+ info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
+ ah->stats.txerr_filt++;
+ }
+ if (ts->ts_status & AR5K_TXERR_XRETRY)
+ ah->stats.txerr_retry++;
+ if (ts->ts_status & AR5K_TXERR_FIFO)
+ ah->stats.txerr_fifo++;
+ } else {
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ info->status.ack_signal = ts->ts_rssi;
+
+ /* count the successful attempt as well */
+ info->status.rates[ts->ts_final_idx].count++;
+ }
+
+ /*
+ * Remove MAC header padding before giving the frame
+ * back to mac80211.
+ */
+ ath5k_remove_padding(skb);
+
+ if (ts->ts_antenna > 0 && ts->ts_antenna < 5)
+ ah->stats.antenna_tx[ts->ts_antenna]++;
+ else
+ ah->stats.antenna_tx[0]++; /* invalid */
+
+ trace_ath5k_tx_complete(ah, skb, txq, ts);
+ ieee80211_tx_status(ah->hw, skb);
+}
+
+static void
+ath5k_tx_processq(struct ath5k_hw *ah, struct ath5k_txq *txq)
+{
+ struct ath5k_tx_status ts = {};
+ struct ath5k_buf *bf, *bf0;
+ struct ath5k_desc *ds;
+ struct sk_buff *skb;
+ int ret;
+
+ spin_lock(&txq->lock);
+ list_for_each_entry_safe(bf, bf0, &txq->q, list) {
+
+ txq->txq_poll_mark = false;
+
+ /* skb might already have been processed last time. */
+ if (bf->skb != NULL) {
+ ds = bf->desc;
+
+ ret = ah->ah_proc_tx_desc(ah, ds, &ts);
+ if (unlikely(ret == -EINPROGRESS))
+ break;
+ else if (unlikely(ret)) {
+ ATH5K_ERR(ah,
+ "error %d while processing "
+ "queue %u\n", ret, txq->qnum);
+ break;
+ }
+
+ skb = bf->skb;
+ bf->skb = NULL;
+
+ dma_unmap_single(ah->dev, bf->skbaddr, skb->len,
+ DMA_TO_DEVICE);
+ ath5k_tx_frame_completed(ah, skb, txq, &ts);
+ }
+
+ /*
+ * It's possible that the hardware can say the buffer is
+ * completed when it hasn't yet loaded the ds_link from
+ * host memory and moved on.
+ * Always keep the last descriptor to avoid HW races...
+ */
+ if (ath5k_hw_get_txdp(ah, txq->qnum) != bf->daddr) {
+ spin_lock(&ah->txbuflock);
+ list_move_tail(&bf->list, &ah->txbuf);
+ ah->txbuf_len++;
+ txq->txq_len--;
+ spin_unlock(&ah->txbuflock);
+ }
+ }
+ spin_unlock(&txq->lock);
+ if (txq->txq_len < ATH5K_TXQ_LEN_LOW && txq->qnum < 4)
+ ieee80211_wake_queue(ah->hw, txq->qnum);
+}
+
+static void
+ath5k_tasklet_tx(unsigned long data)
+{
+ int i;
+ struct ath5k_hw *ah = (void *)data;
+
+ for (i = 0; i < AR5K_NUM_TX_QUEUES; i++)
+ if (ah->txqs[i].setup && (ah->ah_txq_isr & BIT(i)))
+ ath5k_tx_processq(ah, &ah->txqs[i]);
+
+ ah->tx_pending = false;
+ ath5k_set_current_imask(ah);
+}
+
+
+/*****************\
+* Beacon handling *
+\*****************/
+
+/*
+ * Setup the beacon frame for transmit.
+ */
+static int
+ath5k_beacon_setup(struct ath5k_hw *ah, struct ath5k_buf *bf)
+{
+ struct sk_buff *skb = bf->skb;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ath5k_desc *ds;
+ int ret = 0;
+ u8 antenna;
+ u32 flags;
+ const int padsize = 0;
+
+ bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len,
+ DMA_TO_DEVICE);
+ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, "skb %p [data %p len %u] "
+ "skbaddr %llx\n", skb, skb->data, skb->len,
+ (unsigned long long)bf->skbaddr);
+
+ if (dma_mapping_error(ah->dev, bf->skbaddr)) {
+ ATH5K_ERR(ah, "beacon DMA mapping failed\n");
+ dev_kfree_skb_any(skb);
+ bf->skb = NULL;
+ return -EIO;
+ }
+
+ ds = bf->desc;
+ antenna = ah->ah_tx_ant;
+
+ flags = AR5K_TXDESC_NOACK;
+ if (ah->opmode == NL80211_IFTYPE_ADHOC && ath5k_hw_hasveol(ah)) {
+ ds->ds_link = bf->daddr; /* self-linked */
+ flags |= AR5K_TXDESC_VEOL;
+ } else
+ ds->ds_link = 0;
+
+ /*
+ * If we use multiple antennas on AP and use
+ * the Sectored AP scenario, switch antenna every
+ * 4 beacons to make sure everybody hears our AP.
+ * When a client tries to associate, hw will keep
+ * track of the tx antenna to be used for this client
+ * automatically, based on ACKed packets.
+ *
+ * Note: AP still listens and transmits RTS on the
+ * default antenna which is supposed to be an omni.
+ *
+ * Note2: On sectored scenarios it's possible to have
+ * multiple antennas (1 omni -- the default -- and 14
+ * sectors), so if we choose to actually support this
+ * mode, we need to allow the user to set how many antennas
+ * we have and tweak the code below to send beacons
+ * on all of them.
+ */
+ if (ah->ah_ant_mode == AR5K_ANTMODE_SECTOR_AP)
+ antenna = ah->bsent & 4 ? 2 : 1;
+
+
+ /* FIXME: If we are in g mode and rate is a CCK rate
+ * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta
+ * from tx power (value is in dB units already) */
+ ds->ds_data = bf->skbaddr;
+ ret = ah->ah_setup_tx_desc(ah, ds, skb->len,
+ ieee80211_get_hdrlen_from_skb(skb), padsize,
+ AR5K_PKT_TYPE_BEACON, (ah->power_level * 2),
+ ieee80211_get_tx_rate(ah->hw, info)->hw_value,
+ 1, AR5K_TXKEYIX_INVALID,
+ antenna, flags, 0, 0);
+ if (ret)
+ goto err_unmap;
+
+ return 0;
+err_unmap:
+ dma_unmap_single(ah->dev, bf->skbaddr, skb->len, DMA_TO_DEVICE);
+ return ret;
+}
+
+/*
+ * Updates the beacon that is sent by ath5k_beacon_send. For adhoc,
+ * this is called only once at config_bss time, for AP we do it every
+ * SWBA interrupt so that the TIM will reflect buffered frames.
+ *
+ * Called with the beacon lock.
+ */
+int
+ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ int ret;
+ struct ath5k_hw *ah = hw->priv;
+ struct ath5k_vif *avf = (void *)vif->drv_priv;
+ struct sk_buff *skb;
+
+ if (WARN_ON(!vif)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ skb = ieee80211_beacon_get(hw, vif);
+
+ if (!skb) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ath5k_txbuf_free_skb(ah, avf->bbuf);
+ avf->bbuf->skb = skb;
+ ret = ath5k_beacon_setup(ah, avf->bbuf);
+out:
+ return ret;
+}
+
+/*
+ * Transmit a beacon frame at SWBA. Dynamic updates to the
+ * frame contents are done as needed and the slot time is
+ * also adjusted based on current state.
+ *
+ * This is called from software irq context (beacontq tasklets)
+ * or user context from ath5k_beacon_config.
+ */
+static void
+ath5k_beacon_send(struct ath5k_hw *ah)
+{
+ struct ieee80211_vif *vif;
+ struct ath5k_vif *avf;
+ struct ath5k_buf *bf;
+ struct sk_buff *skb;
+ int err;
+
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "in beacon_send\n");
+
+ /*
+ * Check if the previous beacon has gone out. If
+ * not, don't don't try to post another: skip this
+ * period and wait for the next. Missed beacons
+ * indicate a problem and should not occur. If we
+ * miss too many consecutive beacons reset the device.
+ */
+ if (unlikely(ath5k_hw_num_tx_pending(ah, ah->bhalq) != 0)) {
+ ah->bmisscount++;
+ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON,
+ "missed %u consecutive beacons\n", ah->bmisscount);
+ if (ah->bmisscount > 10) { /* NB: 10 is a guess */
+ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON,
+ "stuck beacon time (%u missed)\n",
+ ah->bmisscount);
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
+ "stuck beacon, resetting\n");
+ ieee80211_queue_work(ah->hw, &ah->reset_work);
+ }
+ return;
+ }
+ if (unlikely(ah->bmisscount != 0)) {
+ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON,
+ "resume beacon xmit after %u misses\n",
+ ah->bmisscount);
+ ah->bmisscount = 0;
+ }
+
+ if ((ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs > 1) ||
+ ah->opmode == NL80211_IFTYPE_MESH_POINT) {
+ u64 tsf = ath5k_hw_get_tsf64(ah);
+ u32 tsftu = TSF_TO_TU(tsf);
+ int slot = ((tsftu % ah->bintval) * ATH_BCBUF) / ah->bintval;
+ vif = ah->bslot[(slot + 1) % ATH_BCBUF];
+ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON,
+ "tsf %llx tsftu %x intval %u slot %u vif %p\n",
+ (unsigned long long)tsf, tsftu, ah->bintval, slot, vif);
+ } else /* only one interface */
+ vif = ah->bslot[0];
+
+ if (!vif)
+ return;
+
+ avf = (void *)vif->drv_priv;
+ bf = avf->bbuf;
+
+ /*
+ * Stop any current dma and put the new frame on the queue.
+ * This should never fail since we check above that no frames
+ * are still pending on the queue.
+ */
+ if (unlikely(ath5k_hw_stop_beacon_queue(ah, ah->bhalq))) {
+ ATH5K_WARN(ah, "beacon queue %u didn't start/stop ?\n", ah->bhalq);
+ /* NB: hw still stops DMA, so proceed */
+ }
+
+ /* refresh the beacon for AP or MESH mode */
+ if (ah->opmode == NL80211_IFTYPE_AP ||
+ ah->opmode == NL80211_IFTYPE_MESH_POINT) {
+ err = ath5k_beacon_update(ah->hw, vif);
+ if (err)
+ return;
+ }
+
+ if (unlikely(bf->skb == NULL || ah->opmode == NL80211_IFTYPE_STATION ||
+ ah->opmode == NL80211_IFTYPE_MONITOR)) {
+ ATH5K_WARN(ah, "bf=%p bf_skb=%p\n", bf, bf->skb);
+ return;
+ }
+
+ trace_ath5k_tx(ah, bf->skb, &ah->txqs[ah->bhalq]);
+
+ ath5k_hw_set_txdp(ah, ah->bhalq, bf->daddr);
+ ath5k_hw_start_tx_dma(ah, ah->bhalq);
+ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n",
+ ah->bhalq, (unsigned long long)bf->daddr, bf->desc);
+
+ skb = ieee80211_get_buffered_bc(ah->hw, vif);
+ while (skb) {
+ ath5k_tx_queue(ah->hw, skb, ah->cabq);
+
+ if (ah->cabq->txq_len >= ah->cabq->txq_max)
+ break;
+
+ skb = ieee80211_get_buffered_bc(ah->hw, vif);
+ }
+
+ ah->bsent++;
+}
+
+/**
+ * ath5k_beacon_update_timers - update beacon timers
+ *
+ * @ah: struct ath5k_hw pointer we are operating on
+ * @bc_tsf: the timestamp of the beacon. 0 to reset the TSF. -1 to perform a
+ * beacon timer update based on the current HW TSF.
+ *
+ * Calculate the next target beacon transmit time (TBTT) based on the timestamp
+ * of a received beacon or the current local hardware TSF and write it to the
+ * beacon timer registers.
+ *
+ * This is called in a variety of situations, e.g. when a beacon is received,
+ * when a TSF update has been detected, but also when an new IBSS is created or
+ * when we otherwise know we have to update the timers, but we keep it in this
+ * function to have it all together in one place.
+ */
+void
+ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf)
+{
+ u32 nexttbtt, intval, hw_tu, bc_tu;
+ u64 hw_tsf;
+
+ intval = ah->bintval & AR5K_BEACON_PERIOD;
+ if (ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs > 1) {
+ intval /= ATH_BCBUF; /* staggered multi-bss beacons */
+ if (intval < 15)
+ ATH5K_WARN(ah, "intval %u is too low, min 15\n",
+ intval);
+ }
+ if (WARN_ON(!intval))
+ return;
+
+ /* beacon TSF converted to TU */
+ bc_tu = TSF_TO_TU(bc_tsf);
+
+ /* current TSF converted to TU */
+ hw_tsf = ath5k_hw_get_tsf64(ah);
+ hw_tu = TSF_TO_TU(hw_tsf);
+
+#define FUDGE (AR5K_TUNE_SW_BEACON_RESP + 3)
+ /* We use FUDGE to make sure the next TBTT is ahead of the current TU.
+ * Since we later subtract AR5K_TUNE_SW_BEACON_RESP (10) in the timer
+ * configuration we need to make sure it is bigger than that. */
+
+ if (bc_tsf == -1) {
+ /*
+ * no beacons received, called internally.
+ * just need to refresh timers based on HW TSF.
+ */
+ nexttbtt = roundup(hw_tu + FUDGE, intval);
+ } else if (bc_tsf == 0) {
+ /*
+ * no beacon received, probably called by ath5k_reset_tsf().
+ * reset TSF to start with 0.
+ */
+ nexttbtt = intval;
+ intval |= AR5K_BEACON_RESET_TSF;
+ } else if (bc_tsf > hw_tsf) {
+ /*
+ * beacon received, SW merge happened but HW TSF not yet updated.
+ * not possible to reconfigure timers yet, but next time we
+ * receive a beacon with the same BSSID, the hardware will
+ * automatically update the TSF and then we need to reconfigure
+ * the timers.
+ */
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON,
+ "need to wait for HW TSF sync\n");
+ return;
+ } else {
+ /*
+ * most important case for beacon synchronization between STA.
+ *
+ * beacon received and HW TSF has been already updated by HW.
+ * update next TBTT based on the TSF of the beacon, but make
+ * sure it is ahead of our local TSF timer.
+ */
+ nexttbtt = bc_tu + roundup(hw_tu + FUDGE - bc_tu, intval);
+ }
+#undef FUDGE
+
+ ah->nexttbtt = nexttbtt;
+
+ intval |= AR5K_BEACON_ENA;
+ ath5k_hw_init_beacon(ah, nexttbtt, intval);
+
+ /*
+ * debugging output last in order to preserve the time critical aspect
+ * of this function
+ */
+ if (bc_tsf == -1)
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON,
+ "reconfigured timers based on HW TSF\n");
+ else if (bc_tsf == 0)
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON,
+ "reset HW TSF and timers\n");
+ else
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON,
+ "updated timers based on beacon TSF\n");
+
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON,
+ "bc_tsf %llx hw_tsf %llx bc_tu %u hw_tu %u nexttbtt %u\n",
+ (unsigned long long) bc_tsf,
+ (unsigned long long) hw_tsf, bc_tu, hw_tu, nexttbtt);
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "intval %u %s %s\n",
+ intval & AR5K_BEACON_PERIOD,
+ intval & AR5K_BEACON_ENA ? "AR5K_BEACON_ENA" : "",
+ intval & AR5K_BEACON_RESET_TSF ? "AR5K_BEACON_RESET_TSF" : "");
+}
+
+/**
+ * ath5k_beacon_config - Configure the beacon queues and interrupts
+ *
+ * @ah: struct ath5k_hw pointer we are operating on
+ *
+ * In IBSS mode we use a self-linked tx descriptor if possible. We enable SWBA
+ * interrupts to detect TSF updates only.
+ */
+void
+ath5k_beacon_config(struct ath5k_hw *ah)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ah->block, flags);
+ ah->bmisscount = 0;
+ ah->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA);
+
+ if (ah->enable_beacon) {
+ /*
+ * In IBSS mode we use a self-linked tx descriptor and let the
+ * hardware send the beacons automatically. We have to load it
+ * only once here.
+ * We use the SWBA interrupt only to keep track of the beacon
+ * timers in order to detect automatic TSF updates.
+ */
+ ath5k_beaconq_config(ah);
+
+ ah->imask |= AR5K_INT_SWBA;
+
+ if (ah->opmode == NL80211_IFTYPE_ADHOC) {
+ if (ath5k_hw_hasveol(ah))
+ ath5k_beacon_send(ah);
+ } else
+ ath5k_beacon_update_timers(ah, -1);
+ } else {
+ ath5k_hw_stop_beacon_queue(ah, ah->bhalq);
+ }
+
+ ath5k_hw_set_imr(ah, ah->imask);
+ mmiowb();
+ spin_unlock_irqrestore(&ah->block, flags);
+}
+
+static void ath5k_tasklet_beacon(unsigned long data)
+{
+ struct ath5k_hw *ah = (struct ath5k_hw *) data;
+
+ /*
+ * Software beacon alert--time to send a beacon.
+ *
+ * In IBSS mode we use this interrupt just to
+ * keep track of the next TBTT (target beacon
+ * transmission time) in order to detect whether
+ * automatic TSF updates happened.
+ */
+ if (ah->opmode == NL80211_IFTYPE_ADHOC) {
+ /* XXX: only if VEOL supported */
+ u64 tsf = ath5k_hw_get_tsf64(ah);
+ ah->nexttbtt += ah->bintval;
+ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON,
+ "SWBA nexttbtt: %x hw_tu: %x "
+ "TSF: %llx\n",
+ ah->nexttbtt,
+ TSF_TO_TU(tsf),
+ (unsigned long long) tsf);
+ } else {
+ spin_lock(&ah->block);
+ ath5k_beacon_send(ah);
+ spin_unlock(&ah->block);
+ }
+}
+
+
+/********************\
+* Interrupt handling *
+\********************/
+
+static void
+ath5k_intr_calibration_poll(struct ath5k_hw *ah)
+{
+ if (time_is_before_eq_jiffies(ah->ah_cal_next_ani) &&
+ !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL)) {
+ /* run ANI only when full calibration is not active */
+ ah->ah_cal_next_ani = jiffies +
+ msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI);
+ tasklet_schedule(&ah->ani_tasklet);
+
+ } else if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) {
+ ah->ah_cal_next_full = jiffies +
+ msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL);
+ tasklet_schedule(&ah->calib);
+ }
+ /* we could use SWI to generate enough interrupts to meet our
+ * calibration interval requirements, if necessary:
+ * AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); */
+}
+
+static void
+ath5k_schedule_rx(struct ath5k_hw *ah)
+{
+ ah->rx_pending = true;
+ tasklet_schedule(&ah->rxtq);
+}
+
+static void
+ath5k_schedule_tx(struct ath5k_hw *ah)
+{
+ ah->tx_pending = true;
+ tasklet_schedule(&ah->txtq);
+}
+
+static irqreturn_t
+ath5k_intr(int irq, void *dev_id)
+{
+ struct ath5k_hw *ah = dev_id;
+ enum ath5k_int status;
+ unsigned int counter = 1000;
+
+ if (unlikely(test_bit(ATH_STAT_INVALID, ah->status) ||
+ ((ath5k_get_bus_type(ah) != ATH_AHB) &&
+ !ath5k_hw_is_intr_pending(ah))))
+ return IRQ_NONE;
+
+ do {
+ ath5k_hw_get_isr(ah, &status); /* NB: clears IRQ too */
+ ATH5K_DBG(ah, ATH5K_DEBUG_INTR, "status 0x%x/0x%x\n",
+ status, ah->imask);
+ if (unlikely(status & AR5K_INT_FATAL)) {
+ /*
+ * Fatal errors are unrecoverable.
+ * Typically these are caused by DMA errors.
+ */
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
+ "fatal int, resetting\n");
+ ieee80211_queue_work(ah->hw, &ah->reset_work);
+ } else if (unlikely(status & AR5K_INT_RXORN)) {
+ /*
+ * Receive buffers are full. Either the bus is busy or
+ * the CPU is not fast enough to process all received
+ * frames.
+ * Older chipsets need a reset to come out of this
+ * condition, but we treat it as RX for newer chips.
+ * We don't know exactly which versions need a reset -
+ * this guess is copied from the HAL.
+ */
+ ah->stats.rxorn_intr++;
+ if (ah->ah_mac_srev < AR5K_SREV_AR5212) {
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
+ "rx overrun, resetting\n");
+ ieee80211_queue_work(ah->hw, &ah->reset_work);
+ } else
+ ath5k_schedule_rx(ah);
+ } else {
+ if (status & AR5K_INT_SWBA)
+ tasklet_hi_schedule(&ah->beacontq);
+
+ if (status & AR5K_INT_RXEOL) {
+ /*
+ * NB: the hardware should re-read the link when
+ * RXE bit is written, but it doesn't work at
+ * least on older hardware revs.
+ */
+ ah->stats.rxeol_intr++;
+ }
+ if (status & AR5K_INT_TXURN) {
+ /* bump tx trigger level */
+ ath5k_hw_update_tx_triglevel(ah, true);
+ }
+ if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR))
+ ath5k_schedule_rx(ah);
+ if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC
+ | AR5K_INT_TXERR | AR5K_INT_TXEOL))
+ ath5k_schedule_tx(ah);
+ if (status & AR5K_INT_BMISS) {
+ /* TODO */
+ }
+ if (status & AR5K_INT_MIB) {
+ ah->stats.mib_intr++;
+ ath5k_hw_update_mib_counters(ah);
+ ath5k_ani_mib_intr(ah);
+ }
+ if (status & AR5K_INT_GPIO)
+ tasklet_schedule(&ah->rf_kill.toggleq);
+
+ }
+
+ if (ath5k_get_bus_type(ah) == ATH_AHB)
+ break;
+
+ } while (ath5k_hw_is_intr_pending(ah) && --counter > 0);
+
+ if (ah->rx_pending || ah->tx_pending)
+ ath5k_set_current_imask(ah);
+
+ if (unlikely(!counter))
+ ATH5K_WARN(ah, "too many interrupts, giving up for now\n");
+
+ ath5k_intr_calibration_poll(ah);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Periodically recalibrate the PHY to account
+ * for temperature/environment changes.
+ */
+static void
+ath5k_tasklet_calibrate(unsigned long data)
+{
+ struct ath5k_hw *ah = (void *)data;
+
+ /* Only full calibration for now */
+ ah->ah_cal_mask |= AR5K_CALIBRATION_FULL;
+
+ ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n",
+ ieee80211_frequency_to_channel(ah->curchan->center_freq),
+ ah->curchan->hw_value);
+
+ if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) {
+ /*
+ * Rfgain is out of bounds, reset the chip
+ * to load new gain values.
+ */
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "calibration, resetting\n");
+ ieee80211_queue_work(ah->hw, &ah->reset_work);
+ }
+ if (ath5k_hw_phy_calibrate(ah, ah->curchan))
+ ATH5K_ERR(ah, "calibration of channel %u failed\n",
+ ieee80211_frequency_to_channel(
+ ah->curchan->center_freq));
+
+ /* Noise floor calibration interrupts rx/tx path while I/Q calibration
+ * doesn't.
+ * TODO: We should stop TX here, so that it doesn't interfere.
+ * Note that stopping the queues is not enough to stop TX! */
+ if (time_is_before_eq_jiffies(ah->ah_cal_next_nf)) {
+ ah->ah_cal_next_nf = jiffies +
+ msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_NF);
+ ath5k_hw_update_noise_floor(ah);
+ }
+
+ ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL;
+}
+
+
+static void
+ath5k_tasklet_ani(unsigned long data)
+{
+ struct ath5k_hw *ah = (void *)data;
+
+ ah->ah_cal_mask |= AR5K_CALIBRATION_ANI;
+ ath5k_ani_calibration(ah);
+ ah->ah_cal_mask &= ~AR5K_CALIBRATION_ANI;
+}
+
+
+static void
+ath5k_tx_complete_poll_work(struct work_struct *work)
+{
+ struct ath5k_hw *ah = container_of(work, struct ath5k_hw,
+ tx_complete_work.work);
+ struct ath5k_txq *txq;
+ int i;
+ bool needreset = false;
+
+ mutex_lock(&ah->lock);
+
+ for (i = 0; i < ARRAY_SIZE(ah->txqs); i++) {
+ if (ah->txqs[i].setup) {
+ txq = &ah->txqs[i];
+ spin_lock_bh(&txq->lock);
+ if (txq->txq_len > 1) {
+ if (txq->txq_poll_mark) {
+ ATH5K_DBG(ah, ATH5K_DEBUG_XMIT,
+ "TX queue stuck %d\n",
+ txq->qnum);
+ needreset = true;
+ txq->txq_stuck++;
+ spin_unlock_bh(&txq->lock);
+ break;
+ } else {
+ txq->txq_poll_mark = true;
+ }
+ }
+ spin_unlock_bh(&txq->lock);
+ }
+ }
+
+ if (needreset) {
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
+ "TX queues stuck, resetting\n");
+ ath5k_reset(ah, NULL, true);
+ }
+
+ mutex_unlock(&ah->lock);
+
+ ieee80211_queue_delayed_work(ah->hw, &ah->tx_complete_work,
+ msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT));
+}
+
+
+/*************************\
+* Initialization routines *
+\*************************/
+
+int __devinit
+ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
+{
+ struct ieee80211_hw *hw = ah->hw;
+ struct ath_common *common;
+ int ret;
+ int csz;
+
+ /* Initialize driver private data */
+ SET_IEEE80211_DEV(hw, ah->dev);
+ hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+ IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+ IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_REPORTS_TX_ACK_STATUS;
+
+ hw->wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_MESH_POINT);
+
+ /* both antennas can be configured as RX or TX */
+ hw->wiphy->available_antennas_tx = 0x3;
+ hw->wiphy->available_antennas_rx = 0x3;
+
+ hw->extra_tx_headroom = 2;
+ hw->channel_change_time = 5000;
+
+ /*
+ * Mark the device as detached to avoid processing
+ * interrupts until setup is complete.
+ */
+ __set_bit(ATH_STAT_INVALID, ah->status);
+
+ ah->opmode = NL80211_IFTYPE_STATION;
+ ah->bintval = 1000;
+ mutex_init(&ah->lock);
+ spin_lock_init(&ah->rxbuflock);
+ spin_lock_init(&ah->txbuflock);
+ spin_lock_init(&ah->block);
+ spin_lock_init(&ah->irqlock);
+
+ /* Setup interrupt handler */
+ ret = request_irq(ah->irq, ath5k_intr, IRQF_SHARED, "ath", ah);
+ if (ret) {
+ ATH5K_ERR(ah, "request_irq failed\n");
+ goto err;
+ }
+
+ common = ath5k_hw_common(ah);
+ common->ops = &ath5k_common_ops;
+ common->bus_ops = bus_ops;
+ common->ah = ah;
+ common->hw = hw;
+ common->priv = ah;
+ common->clockrate = 40;
+
+ /*
+ * Cache line size is used to size and align various
+ * structures used to communicate with the hardware.
+ */
+ ath5k_read_cachesize(common, &csz);
+ common->cachelsz = csz << 2; /* convert to bytes */
+
+ spin_lock_init(&common->cc_lock);
+
+ /* Initialize device */
+ ret = ath5k_hw_init(ah);
+ if (ret)
+ goto err_irq;
+
+ /* set up multi-rate retry capabilities */
+ if (ah->ah_version == AR5K_AR5212) {
+ hw->max_rates = 4;
+ hw->max_rate_tries = max(AR5K_INIT_RETRY_SHORT,
+ AR5K_INIT_RETRY_LONG);
+ }
+
+ hw->vif_data_size = sizeof(struct ath5k_vif);
+
+ /* Finish private driver data initialization */
+ ret = ath5k_init(hw);
+ if (ret)
+ goto err_ah;
+
+ ATH5K_INFO(ah, "Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n",
+ ath5k_chip_name(AR5K_VERSION_MAC, ah->ah_mac_srev),
+ ah->ah_mac_srev,
+ ah->ah_phy_revision);
+
+ if (!ah->ah_single_chip) {
+ /* Single chip radio (!RF5111) */
+ if (ah->ah_radio_5ghz_revision &&
+ !ah->ah_radio_2ghz_revision) {
+ /* No 5GHz support -> report 2GHz radio */
+ if (!test_bit(AR5K_MODE_11A,
+ ah->ah_capabilities.cap_mode)) {
+ ATH5K_INFO(ah, "RF%s 2GHz radio found (0x%x)\n",
+ ath5k_chip_name(AR5K_VERSION_RAD,
+ ah->ah_radio_5ghz_revision),
+ ah->ah_radio_5ghz_revision);
+ /* No 2GHz support (5110 and some
+ * 5GHz only cards) -> report 5GHz radio */
+ } else if (!test_bit(AR5K_MODE_11B,
+ ah->ah_capabilities.cap_mode)) {
+ ATH5K_INFO(ah, "RF%s 5GHz radio found (0x%x)\n",
+ ath5k_chip_name(AR5K_VERSION_RAD,
+ ah->ah_radio_5ghz_revision),
+ ah->ah_radio_5ghz_revision);
+ /* Multiband radio */
+ } else {
+ ATH5K_INFO(ah, "RF%s multiband radio found"
+ " (0x%x)\n",
+ ath5k_chip_name(AR5K_VERSION_RAD,
+ ah->ah_radio_5ghz_revision),
+ ah->ah_radio_5ghz_revision);
+ }
+ }
+ /* Multi chip radio (RF5111 - RF2111) ->
+ * report both 2GHz/5GHz radios */
+ else if (ah->ah_radio_5ghz_revision &&
+ ah->ah_radio_2ghz_revision) {
+ ATH5K_INFO(ah, "RF%s 5GHz radio found (0x%x)\n",
+ ath5k_chip_name(AR5K_VERSION_RAD,
+ ah->ah_radio_5ghz_revision),
+ ah->ah_radio_5ghz_revision);
+ ATH5K_INFO(ah, "RF%s 2GHz radio found (0x%x)\n",
+ ath5k_chip_name(AR5K_VERSION_RAD,
+ ah->ah_radio_2ghz_revision),
+ ah->ah_radio_2ghz_revision);
+ }
+ }
+
+ ath5k_debug_init_device(ah);
+
+ /* ready to process interrupts */
+ __clear_bit(ATH_STAT_INVALID, ah->status);
+
+ return 0;
+err_ah:
+ ath5k_hw_deinit(ah);
+err_irq:
+ free_irq(ah->irq, ah);
+err:
+ return ret;
+}
+
+static int
+ath5k_stop_locked(struct ath5k_hw *ah)
+{
+
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "invalid %u\n",
+ test_bit(ATH_STAT_INVALID, ah->status));
+
+ /*
+ * Shutdown the hardware and driver:
+ * stop output from above
+ * disable interrupts
+ * turn off timers
+ * turn off the radio
+ * clear transmit machinery
+ * clear receive machinery
+ * drain and release tx queues
+ * reclaim beacon resources
+ * power down hardware
+ *
+ * Note that some of this work is not possible if the
+ * hardware is gone (invalid).
+ */
+ ieee80211_stop_queues(ah->hw);
+
+ if (!test_bit(ATH_STAT_INVALID, ah->status)) {
+ ath5k_led_off(ah);
+ ath5k_hw_set_imr(ah, 0);
+ synchronize_irq(ah->irq);
+ ath5k_rx_stop(ah);
+ ath5k_hw_dma_stop(ah);
+ ath5k_drain_tx_buffs(ah);
+ ath5k_hw_phy_disable(ah);
+ }
+
+ return 0;
+}
+
+int ath5k_start(struct ieee80211_hw *hw)
+{
+ struct ath5k_hw *ah = hw->priv;
+ struct ath_common *common = ath5k_hw_common(ah);
+ int ret, i;
+
+ mutex_lock(&ah->lock);
+
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "mode %d\n", ah->opmode);
+
+ /*
+ * Stop anything previously setup. This is safe
+ * no matter this is the first time through or not.
+ */
+ ath5k_stop_locked(ah);
+
+ /*
+ * The basic interface to setting the hardware in a good
+ * state is ``reset''. On return the hardware is known to
+ * be powered up and with interrupts disabled. This must
+ * be followed by initialization of the appropriate bits
+ * and then setup of the interrupt mask.
+ */
+ ah->curchan = ah->hw->conf.channel;
+ ah->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
+ AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
+ AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB;
+
+ ret = ath5k_reset(ah, NULL, false);
+ if (ret)
+ goto done;
+
+ ath5k_rfkill_hw_start(ah);
+
+ /*
+ * Reset the key cache since some parts do not reset the
+ * contents on initial power up or resume from suspend.
+ */
+ for (i = 0; i < common->keymax; i++)
+ ath_hw_keyreset(common, (u16) i);
+
+ /* Use higher rates for acks instead of base
+ * rate */
+ ah->ah_ack_bitrate_high = true;
+
+ for (i = 0; i < ARRAY_SIZE(ah->bslot); i++)
+ ah->bslot[i] = NULL;
+
+ ret = 0;
+done:
+ mmiowb();
+ mutex_unlock(&ah->lock);
+
+ ieee80211_queue_delayed_work(ah->hw, &ah->tx_complete_work,
+ msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT));
+
+ return ret;
+}
+
+static void ath5k_stop_tasklets(struct ath5k_hw *ah)
+{
+ ah->rx_pending = false;
+ ah->tx_pending = false;
+ tasklet_kill(&ah->rxtq);
+ tasklet_kill(&ah->txtq);
+ tasklet_kill(&ah->calib);
+ tasklet_kill(&ah->beacontq);
+ tasklet_kill(&ah->ani_tasklet);
+}
+
+/*
+ * Stop the device, grabbing the top-level lock to protect
+ * against concurrent entry through ath5k_init (which can happen
+ * if another thread does a system call and the thread doing the
+ * stop is preempted).
+ */
+void ath5k_stop(struct ieee80211_hw *hw)
+{
+ struct ath5k_hw *ah = hw->priv;
+ int ret;
+
+ mutex_lock(&ah->lock);
+ ret = ath5k_stop_locked(ah);
+ if (ret == 0 && !test_bit(ATH_STAT_INVALID, ah->status)) {
+ /*
+ * Don't set the card in full sleep mode!
+ *
+ * a) When the device is in this state it must be carefully
+ * woken up or references to registers in the PCI clock
+ * domain may freeze the bus (and system). This varies
+ * by chip and is mostly an issue with newer parts
+ * (madwifi sources mentioned srev >= 0x78) that go to
+ * sleep more quickly.
+ *
+ * b) On older chips full sleep results a weird behaviour
+ * during wakeup. I tested various cards with srev < 0x78
+ * and they don't wake up after module reload, a second
+ * module reload is needed to bring the card up again.
+ *
+ * Until we figure out what's going on don't enable
+ * full chip reset on any chip (this is what Legacy HAL
+ * and Sam's HAL do anyway). Instead Perform a full reset
+ * on the device (same as initial state after attach) and
+ * leave it idle (keep MAC/BB on warm reset) */
+ ret = ath5k_hw_on_hold(ah);
+
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
+ "putting device to sleep\n");
+ }
+
+ mmiowb();
+ mutex_unlock(&ah->lock);
+
+ ath5k_stop_tasklets(ah);
+
+ cancel_delayed_work_sync(&ah->tx_complete_work);
+
+ ath5k_rfkill_hw_stop(ah);
+}
+
+/*
+ * Reset the hardware. If chan is not NULL, then also pause rx/tx
+ * and change to the given channel.
+ *
+ * This should be called with ah->lock.
+ */
+static int
+ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,
+ bool skip_pcu)
+{
+ struct ath_common *common = ath5k_hw_common(ah);
+ int ret, ani_mode;
+ bool fast;
+
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n");
+
+ ath5k_hw_set_imr(ah, 0);
+ synchronize_irq(ah->irq);
+ ath5k_stop_tasklets(ah);
+
+ /* Save ani mode and disable ANI during
+ * reset. If we don't we might get false
+ * PHY error interrupts. */
+ ani_mode = ah->ani_state.ani_mode;
+ ath5k_ani_init(ah, ATH5K_ANI_MODE_OFF);
+
+ /* We are going to empty hw queues
+ * so we should also free any remaining
+ * tx buffers */
+ ath5k_drain_tx_buffs(ah);
+ if (chan)
+ ah->curchan = chan;
+
+ fast = ((chan != NULL) && modparam_fastchanswitch) ? 1 : 0;
+
+ ret = ath5k_hw_reset(ah, ah->opmode, ah->curchan, fast, skip_pcu);
+ if (ret) {
+ ATH5K_ERR(ah, "can't reset hardware (%d)\n", ret);
+ goto err;
+ }
+
+ ret = ath5k_rx_start(ah);
+ if (ret) {
+ ATH5K_ERR(ah, "can't start recv logic\n");
+ goto err;
+ }
+
+ ath5k_ani_init(ah, ani_mode);
+
+ ah->ah_cal_next_full = jiffies + msecs_to_jiffies(100);
+ ah->ah_cal_next_ani = jiffies;
+ ah->ah_cal_next_nf = jiffies;
+ ewma_init(&ah->ah_beacon_rssi_avg, 1024, 8);
+
+ /* clear survey data and cycle counters */
+ memset(&ah->survey, 0, sizeof(ah->survey));
+ spin_lock_bh(&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_bh(&common->cc_lock);
+
+ /*
+ * Change channels and update the h/w rate map if we're switching;
+ * e.g. 11a to 11b/g.
+ *
+ * We may be doing a reset in response to an ioctl that changes the
+ * channel so update any state that might change as a result.
+ *
+ * XXX needed?
+ */
+/* ath5k_chan_change(ah, c); */
+
+ ath5k_beacon_config(ah);
+ /* intrs are enabled by ath5k_beacon_config */
+
+ ieee80211_wake_queues(ah->hw);
+
+ return 0;
+err:
+ return ret;
+}
+
+static void ath5k_reset_work(struct work_struct *work)
+{
+ struct ath5k_hw *ah = container_of(work, struct ath5k_hw,
+ reset_work);
+
+ mutex_lock(&ah->lock);
+ ath5k_reset(ah, NULL, true);
+ mutex_unlock(&ah->lock);
+}
+
+static int __devinit
+ath5k_init(struct ieee80211_hw *hw)
+{
+
+ struct ath5k_hw *ah = hw->priv;
+ struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
+ struct ath5k_txq *txq;
+ u8 mac[ETH_ALEN] = {};
+ int ret;
+
+
+ /*
+ * Check if the MAC has multi-rate retry support.
+ * We do this by trying to setup a fake extended
+ * descriptor. MACs that don't have support will
+ * return false w/o doing anything. MACs that do
+ * support it will return true w/o doing anything.
+ */
+ ret = ath5k_hw_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0);
+
+ if (ret < 0)
+ goto err;
+ if (ret > 0)
+ __set_bit(ATH_STAT_MRRETRY, ah->status);
+
+ /*
+ * Collect the channel list. The 802.11 layer
+ * is responsible for filtering this list based
+ * on settings like the phy mode and regulatory
+ * domain restrictions.
+ */
+ ret = ath5k_setup_bands(hw);
+ if (ret) {
+ ATH5K_ERR(ah, "can't get channels\n");
+ goto err;
+ }
+
+ /*
+ * Allocate tx+rx descriptors and populate the lists.
+ */
+ ret = ath5k_desc_alloc(ah);
+ if (ret) {
+ ATH5K_ERR(ah, "can't allocate descriptors\n");
+ goto err;
+ }
+
+ /*
+ * Allocate hardware transmit queues: one queue for
+ * beacon frames and one data queue for each QoS
+ * priority. Note that hw functions handle resetting
+ * these queues at the needed time.
+ */
+ ret = ath5k_beaconq_setup(ah);
+ if (ret < 0) {
+ ATH5K_ERR(ah, "can't setup a beacon xmit queue\n");
+ goto err_desc;
+ }
+ ah->bhalq = ret;
+ ah->cabq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_CAB, 0);
+ if (IS_ERR(ah->cabq)) {
+ ATH5K_ERR(ah, "can't setup cab queue\n");
+ ret = PTR_ERR(ah->cabq);
+ goto err_bhal;
+ }
+
+ /* 5211 and 5212 usually support 10 queues but we better rely on the
+ * capability information */
+ if (ah->ah_capabilities.cap_queues.q_tx_num >= 6) {
+ /* This order matches mac80211's queue priority, so we can
+ * directly use the mac80211 queue number without any mapping */
+ txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_VO);
+ if (IS_ERR(txq)) {
+ ATH5K_ERR(ah, "can't setup xmit queue\n");
+ ret = PTR_ERR(txq);
+ goto err_queues;
+ }
+ txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_VI);
+ if (IS_ERR(txq)) {
+ ATH5K_ERR(ah, "can't setup xmit queue\n");
+ ret = PTR_ERR(txq);
+ goto err_queues;
+ }
+ txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BE);
+ if (IS_ERR(txq)) {
+ ATH5K_ERR(ah, "can't setup xmit queue\n");
+ ret = PTR_ERR(txq);
+ goto err_queues;
+ }
+ txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK);
+ if (IS_ERR(txq)) {
+ ATH5K_ERR(ah, "can't setup xmit queue\n");
+ ret = PTR_ERR(txq);
+ goto err_queues;
+ }
+ hw->queues = 4;
+ } else {
+ /* older hardware (5210) can only support one data queue */
+ txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BE);
+ if (IS_ERR(txq)) {
+ ATH5K_ERR(ah, "can't setup xmit queue\n");
+ ret = PTR_ERR(txq);
+ goto err_queues;
+ }
+ hw->queues = 1;
+ }
+
+ tasklet_init(&ah->rxtq, ath5k_tasklet_rx, (unsigned long)ah);
+ tasklet_init(&ah->txtq, ath5k_tasklet_tx, (unsigned long)ah);
+ tasklet_init(&ah->calib, ath5k_tasklet_calibrate, (unsigned long)ah);
+ tasklet_init(&ah->beacontq, ath5k_tasklet_beacon, (unsigned long)ah);
+ tasklet_init(&ah->ani_tasklet, ath5k_tasklet_ani, (unsigned long)ah);
+
+ INIT_WORK(&ah->reset_work, ath5k_reset_work);
+ INIT_DELAYED_WORK(&ah->tx_complete_work, ath5k_tx_complete_poll_work);
+
+ ret = ath5k_hw_common(ah)->bus_ops->eeprom_read_mac(ah, mac);
+ if (ret) {
+ ATH5K_ERR(ah, "unable to read address from EEPROM\n");
+ goto err_queues;
+ }
+
+ SET_IEEE80211_PERM_ADDR(hw, mac);
+ /* All MAC address bits matter for ACKs */
+ ath5k_update_bssid_mask_and_opmode(ah, NULL);
+
+ regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain;
+ ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier);
+ if (ret) {
+ ATH5K_ERR(ah, "can't initialize regulatory system\n");
+ goto err_queues;
+ }
+
+ ret = ieee80211_register_hw(hw);
+ if (ret) {
+ ATH5K_ERR(ah, "can't register ieee80211 hw\n");
+ goto err_queues;
+ }
+
+ if (!ath_is_world_regd(regulatory))
+ regulatory_hint(hw->wiphy, regulatory->alpha2);
+
+ ath5k_init_leds(ah);
+
+ ath5k_sysfs_register(ah);
+
+ return 0;
+err_queues:
+ ath5k_txq_release(ah);
+err_bhal:
+ ath5k_hw_release_tx_queue(ah, ah->bhalq);
+err_desc:
+ ath5k_desc_free(ah);
+err:
+ return ret;
+}
+
+void
+ath5k_deinit_ah(struct ath5k_hw *ah)
+{
+ struct ieee80211_hw *hw = ah->hw;
+
+ /*
+ * NB: the order of these is important:
+ * o call the 802.11 layer before detaching ath5k_hw to
+ * ensure callbacks into the driver to delete global
+ * key cache entries can be handled
+ * o reclaim the tx queue data structures after calling
+ * the 802.11 layer as we'll get called back to reclaim
+ * node state and potentially want to use them
+ * o to cleanup the tx queues the hal is called, so detach
+ * it last
+ * XXX: ??? detach ath5k_hw ???
+ * Other than that, it's straightforward...
+ */
+ ieee80211_unregister_hw(hw);
+ ath5k_desc_free(ah);
+ ath5k_txq_release(ah);
+ ath5k_hw_release_tx_queue(ah, ah->bhalq);
+ ath5k_unregister_leds(ah);
+
+ ath5k_sysfs_unregister(ah);
+ /*
+ * NB: can't reclaim these until after ieee80211_ifdetach
+ * returns because we'll get called back to reclaim node
+ * state and potentially want to use them.
+ */
+ ath5k_hw_deinit(ah);
+ free_irq(ah->irq, ah);
+}
+
+bool
+ath5k_any_vif_assoc(struct ath5k_hw *ah)
+{
+ struct ath5k_vif_iter_data iter_data;
+ iter_data.hw_macaddr = NULL;
+ iter_data.any_assoc = false;
+ iter_data.need_set_hw_addr = false;
+ iter_data.found_active = true;
+
+ ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter,
+ &iter_data);
+ return iter_data.any_assoc;
+}
+
+void
+ath5k_set_beacon_filter(struct ieee80211_hw *hw, bool enable)
+{
+ struct ath5k_hw *ah = hw->priv;
+ u32 rfilt;
+ rfilt = ath5k_hw_get_rx_filter(ah);
+ if (enable)
+ rfilt |= AR5K_RX_FILTER_BEACON;
+ else
+ rfilt &= ~AR5K_RX_FILTER_BEACON;
+ ath5k_hw_set_rx_filter(ah, rfilt);
+ ah->filter_flags = rfilt;
+}
diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h
new file mode 100644
index 0000000..6c94c7f
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/base.h
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * 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 NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ */
+
+/*
+ * Definitions for the Atheros Wireless LAN controller driver.
+ */
+#ifndef _DEV_ATH5K_BASE_H
+#define _DEV_ATH5K_BASE_H
+
+struct ieee80211_vif;
+struct ieee80211_hw;
+struct ath5k_hw;
+struct ath5k_txq;
+struct ieee80211_channel;
+struct ath_bus_ops;
+enum nl80211_iftype;
+
+enum ath5k_srev_type {
+ AR5K_VERSION_MAC,
+ AR5K_VERSION_RAD,
+};
+
+struct ath5k_srev_name {
+ const char *sr_name;
+ enum ath5k_srev_type sr_type;
+ u_int sr_val;
+};
+
+struct ath5k_buf {
+ struct list_head list;
+ struct ath5k_desc *desc; /* virtual addr of desc */
+ dma_addr_t daddr; /* physical addr of desc */
+ struct sk_buff *skb; /* skbuff for buf */
+ dma_addr_t skbaddr;/* physical addr of skb data */
+};
+
+struct ath5k_vif {
+ bool assoc; /* are we associated or not */
+ enum nl80211_iftype opmode;
+ int bslot;
+ struct ath5k_buf *bbuf; /* beacon buffer */
+};
+
+struct ath5k_vif_iter_data {
+ const u8 *hw_macaddr;
+ u8 mask[ETH_ALEN];
+ u8 active_mac[ETH_ALEN]; /* first active MAC */
+ bool need_set_hw_addr;
+ bool found_active;
+ bool any_assoc;
+ enum nl80211_iftype opmode;
+ int n_stas;
+};
+
+void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif);
+bool ath5k_any_vif_assoc(struct ath5k_hw *ah);
+
+int ath5k_start(struct ieee80211_hw *hw);
+void ath5k_stop(struct ieee80211_hw *hw);
+
+void ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf);
+int ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+void ath5k_beacon_config(struct ath5k_hw *ah);
+void ath5k_set_beacon_filter(struct ieee80211_hw *hw, bool enable);
+
+void ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah,
+ struct ieee80211_vif *vif);
+int ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan);
+void ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
+void ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
+void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
+ struct ath5k_txq *txq);
+
+const char *ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val);
+
+int ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops);
+void ath5k_deinit_ah(struct ath5k_hw *ah);
+
+/* Check whether BSSID mask is supported */
+#define ath5k_hw_hasbssidmask(_ah) (ah->ah_version == AR5K_AR5212)
+
+/* Check whether virtual EOL is supported */
+#define ath5k_hw_hasveol(_ah) (ah->ah_version != AR5K_AR5210)
+
+#endif /* _DEV_ATH5K_BASE_H */
diff --git a/drivers/net/wireless/ath/ath5k/caps.c b/drivers/net/wireless/ath/ath5k/caps.c
new file mode 100644
index 0000000..810fba9
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/caps.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
+ * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com>
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ */
+
+/**************\
+* Capabilities *
+\**************/
+
+#include "ath5k.h"
+#include "reg.h"
+#include "debug.h"
+#include "../regd.h"
+
+/*
+ * Fill the capabilities struct
+ * TODO: Merge this with EEPROM code when we are done with it
+ */
+int ath5k_hw_set_capabilities(struct ath5k_hw *ah)
+{
+ struct ath5k_capabilities *caps = &ah->ah_capabilities;
+ u16 ee_header;
+
+ /* Capabilities stored in the EEPROM */
+ ee_header = caps->cap_eeprom.ee_header;
+
+ if (ah->ah_version == AR5K_AR5210) {
+ /*
+ * Set radio capabilities
+ * (The AR5110 only supports the middle 5GHz band)
+ */
+ caps->cap_range.range_5ghz_min = 5120;
+ caps->cap_range.range_5ghz_max = 5430;
+ caps->cap_range.range_2ghz_min = 0;
+ caps->cap_range.range_2ghz_max = 0;
+
+ /* Set supported modes */
+ __set_bit(AR5K_MODE_11A, caps->cap_mode);
+ } else {
+ /*
+ * XXX The transceiver supports frequencies from 4920 to 6100MHz
+ * XXX and from 2312 to 2732MHz. There are problems with the
+ * XXX current ieee80211 implementation because the IEEE
+ * XXX channel mapping does not support negative channel
+ * XXX numbers (2312MHz is channel -19). Of course, this
+ * XXX doesn't matter because these channels are out of the
+ * XXX legal range.
+ */
+
+ /*
+ * Set radio capabilities
+ */
+
+ if (AR5K_EEPROM_HDR_11A(ee_header)) {
+ if (ath_is_49ghz_allowed(caps->cap_eeprom.ee_regdomain))
+ caps->cap_range.range_5ghz_min = 4920;
+ else
+ caps->cap_range.range_5ghz_min = 5005;
+ caps->cap_range.range_5ghz_max = 6100;
+
+ /* Set supported modes */
+ __set_bit(AR5K_MODE_11A, caps->cap_mode);
+ }
+
+ /* Enable 802.11b if a 2GHz capable radio (2111/5112) is
+ * connected */
+ if (AR5K_EEPROM_HDR_11B(ee_header) ||
+ (AR5K_EEPROM_HDR_11G(ee_header) &&
+ ah->ah_version != AR5K_AR5211)) {
+ /* 2312 */
+ caps->cap_range.range_2ghz_min = 2412;
+ caps->cap_range.range_2ghz_max = 2732;
+
+ if (AR5K_EEPROM_HDR_11B(ee_header))
+ __set_bit(AR5K_MODE_11B, caps->cap_mode);
+
+ if (AR5K_EEPROM_HDR_11G(ee_header) &&
+ ah->ah_version != AR5K_AR5211)
+ __set_bit(AR5K_MODE_11G, caps->cap_mode);
+ }
+ }
+
+ if ((ah->ah_radio_5ghz_revision & 0xf0) == AR5K_SREV_RAD_2112)
+ __clear_bit(AR5K_MODE_11A, caps->cap_mode);
+
+ /* Set number of supported TX queues */
+ if (ah->ah_version == AR5K_AR5210)
+ caps->cap_queues.q_tx_num = AR5K_NUM_TX_QUEUES_NOQCU;
+ else
+ caps->cap_queues.q_tx_num = AR5K_NUM_TX_QUEUES;
+
+ /* newer hardware has PHY error counters */
+ if (ah->ah_mac_srev >= AR5K_SREV_AR5213A)
+ caps->cap_has_phyerr_counters = true;
+ else
+ caps->cap_has_phyerr_counters = false;
+
+ return 0;
+}
+
+/*
+ * TODO: Following functions should be part of a new function
+ * set_capability
+ */
+
+int ath5k_hw_enable_pspoll(struct ath5k_hw *ah, u8 *bssid,
+ u16 assoc_id)
+{
+ if (ah->ah_version == AR5K_AR5210) {
+ AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1,
+ AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA);
+ return 0;
+ }
+
+ return -EIO;
+}
+
+int ath5k_hw_disable_pspoll(struct ath5k_hw *ah)
+{
+ if (ah->ah_version == AR5K_AR5210) {
+ AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1,
+ AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA);
+ return 0;
+ }
+
+ return -EIO;
+}
diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c
new file mode 100644
index 0000000..8c5ce8b
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/debug.c
@@ -0,0 +1,1033 @@
+/*
+ * Copyright (c) 2007-2008 Bruno Randolf <bruno@thinktube.com>
+ *
+ * This file is free software: you may copy, redistribute 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 file 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 <http://www.gnu.org/licenses/>.
+ *
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2005 Atheros Communications, Inc.
+ * Copyright (c) 2006 Devicescape Software, Inc.
+ * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
+ * Copyright (c) 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * 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 NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ */
+#include <linux/export.h>
+#include <linux/moduleparam.h>
+
+#include <linux/seq_file.h>
+#include <linux/list.h>
+#include "debug.h"
+#include "ath5k.h"
+#include "reg.h"
+#include "base.h"
+
+static unsigned int ath5k_debug;
+module_param_named(debug, ath5k_debug, uint, 0);
+
+
+static int ath5k_debugfs_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+
+/* debugfs: registers */
+
+struct reg {
+ const char *name;
+ int addr;
+};
+
+#define REG_STRUCT_INIT(r) { #r, r }
+
+/* just a few random registers, might want to add more */
+static const struct reg regs[] = {
+ REG_STRUCT_INIT(AR5K_CR),
+ REG_STRUCT_INIT(AR5K_RXDP),
+ REG_STRUCT_INIT(AR5K_CFG),
+ REG_STRUCT_INIT(AR5K_IER),
+ REG_STRUCT_INIT(AR5K_BCR),
+ REG_STRUCT_INIT(AR5K_RTSD0),
+ REG_STRUCT_INIT(AR5K_RTSD1),
+ REG_STRUCT_INIT(AR5K_TXCFG),
+ REG_STRUCT_INIT(AR5K_RXCFG),
+ REG_STRUCT_INIT(AR5K_RXJLA),
+ REG_STRUCT_INIT(AR5K_MIBC),
+ REG_STRUCT_INIT(AR5K_TOPS),
+ REG_STRUCT_INIT(AR5K_RXNOFRM),
+ REG_STRUCT_INIT(AR5K_TXNOFRM),
+ REG_STRUCT_INIT(AR5K_RPGTO),
+ REG_STRUCT_INIT(AR5K_RFCNT),
+ REG_STRUCT_INIT(AR5K_MISC),
+ REG_STRUCT_INIT(AR5K_QCUDCU_CLKGT),
+ REG_STRUCT_INIT(AR5K_ISR),
+ REG_STRUCT_INIT(AR5K_PISR),
+ REG_STRUCT_INIT(AR5K_SISR0),
+ REG_STRUCT_INIT(AR5K_SISR1),
+ REG_STRUCT_INIT(AR5K_SISR2),
+ REG_STRUCT_INIT(AR5K_SISR3),
+ REG_STRUCT_INIT(AR5K_SISR4),
+ REG_STRUCT_INIT(AR5K_IMR),
+ REG_STRUCT_INIT(AR5K_PIMR),
+ REG_STRUCT_INIT(AR5K_SIMR0),
+ REG_STRUCT_INIT(AR5K_SIMR1),
+ REG_STRUCT_INIT(AR5K_SIMR2),
+ REG_STRUCT_INIT(AR5K_SIMR3),
+ REG_STRUCT_INIT(AR5K_SIMR4),
+ REG_STRUCT_INIT(AR5K_DCM_ADDR),
+ REG_STRUCT_INIT(AR5K_DCCFG),
+ REG_STRUCT_INIT(AR5K_CCFG),
+ REG_STRUCT_INIT(AR5K_CPC0),
+ REG_STRUCT_INIT(AR5K_CPC1),
+ REG_STRUCT_INIT(AR5K_CPC2),
+ REG_STRUCT_INIT(AR5K_CPC3),
+ REG_STRUCT_INIT(AR5K_CPCOVF),
+ REG_STRUCT_INIT(AR5K_RESET_CTL),
+ REG_STRUCT_INIT(AR5K_SLEEP_CTL),
+ REG_STRUCT_INIT(AR5K_INTPEND),
+ REG_STRUCT_INIT(AR5K_SFR),
+ REG_STRUCT_INIT(AR5K_PCICFG),
+ REG_STRUCT_INIT(AR5K_GPIOCR),
+ REG_STRUCT_INIT(AR5K_GPIODO),
+ REG_STRUCT_INIT(AR5K_SREV),
+};
+
+static void *reg_start(struct seq_file *seq, loff_t *pos)
+{
+ return *pos < ARRAY_SIZE(regs) ? (void *)®s[*pos] : NULL;
+}
+
+static void reg_stop(struct seq_file *seq, void *p)
+{
+ /* nothing to do */
+}
+
+static void *reg_next(struct seq_file *seq, void *p, loff_t *pos)
+{
+ ++*pos;
+ return *pos < ARRAY_SIZE(regs) ? (void *)®s[*pos] : NULL;
+}
+
+static int reg_show(struct seq_file *seq, void *p)
+{
+ struct ath5k_hw *ah = seq->private;
+ struct reg *r = p;
+ seq_printf(seq, "%-25s0x%08x\n", r->name,
+ ath5k_hw_reg_read(ah, r->addr));
+ return 0;
+}
+
+static const struct seq_operations register_seq_ops = {
+ .start = reg_start,
+ .next = reg_next,
+ .stop = reg_stop,
+ .show = reg_show
+};
+
+static int open_file_registers(struct inode *inode, struct file *file)
+{
+ struct seq_file *s;
+ int res;
+ res = seq_open(file, ®ister_seq_ops);
+ if (res == 0) {
+ s = file->private_data;
+ s->private = inode->i_private;
+ }
+ return res;
+}
+
+static const struct file_operations fops_registers = {
+ .open = open_file_registers,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+ .owner = THIS_MODULE,
+};
+
+
+/* debugfs: beacons */
+
+static ssize_t read_file_beacon(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath5k_hw *ah = file->private_data;
+ char buf[500];
+ unsigned int len = 0;
+ unsigned int v;
+ u64 tsf;
+
+ v = ath5k_hw_reg_read(ah, AR5K_BEACON);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%-24s0x%08x\tintval: %d\tTIM: 0x%x\n",
+ "AR5K_BEACON", v, v & AR5K_BEACON_PERIOD,
+ (v & AR5K_BEACON_TIM) >> AR5K_BEACON_TIM_S);
+
+ len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\n",
+ "AR5K_LAST_TSTP", ath5k_hw_reg_read(ah, AR5K_LAST_TSTP));
+
+ len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\n\n",
+ "AR5K_BEACON_CNT", ath5k_hw_reg_read(ah, AR5K_BEACON_CNT));
+
+ v = ath5k_hw_reg_read(ah, AR5K_TIMER0);
+ len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\tTU: %08x\n",
+ "AR5K_TIMER0 (TBTT)", v, v);
+
+ v = ath5k_hw_reg_read(ah, AR5K_TIMER1);
+ len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\tTU: %08x\n",
+ "AR5K_TIMER1 (DMA)", v, v >> 3);
+
+ v = ath5k_hw_reg_read(ah, AR5K_TIMER2);
+ len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\tTU: %08x\n",
+ "AR5K_TIMER2 (SWBA)", v, v >> 3);
+
+ v = ath5k_hw_reg_read(ah, AR5K_TIMER3);
+ len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\tTU: %08x\n",
+ "AR5K_TIMER3 (ATIM)", v, v);
+
+ tsf = ath5k_hw_get_tsf64(ah);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "TSF\t\t0x%016llx\tTU: %08x\n",
+ (unsigned long long)tsf, TSF_TO_TU(tsf));
+
+ if (len > sizeof(buf))
+ len = sizeof(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_beacon(struct file *file,
+ const char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath5k_hw *ah = file->private_data;
+ char buf[20];
+
+ if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+ return -EFAULT;
+
+ if (strncmp(buf, "disable", 7) == 0) {
+ AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE);
+ printk(KERN_INFO "debugfs disable beacons\n");
+ } else if (strncmp(buf, "enable", 6) == 0) {
+ AR5K_REG_ENABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE);
+ printk(KERN_INFO "debugfs enable beacons\n");
+ }
+ return count;
+}
+
+static const struct file_operations fops_beacon = {
+ .read = read_file_beacon,
+ .write = write_file_beacon,
+ .open = ath5k_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+
+/* debugfs: reset */
+
+static ssize_t write_file_reset(struct file *file,
+ const char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath5k_hw *ah = file->private_data;
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "debug file triggered reset\n");
+ ieee80211_queue_work(ah->hw, &ah->reset_work);
+ return count;
+}
+
+static const struct file_operations fops_reset = {
+ .write = write_file_reset,
+ .open = ath5k_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+};
+
+
+/* debugfs: debug level */
+
+static const struct {
+ enum ath5k_debug_level level;
+ const char *name;
+ const char *desc;
+} dbg_info[] = {
+ { ATH5K_DEBUG_RESET, "reset", "reset and initialization" },
+ { ATH5K_DEBUG_INTR, "intr", "interrupt handling" },
+ { ATH5K_DEBUG_MODE, "mode", "mode init/setup" },
+ { ATH5K_DEBUG_XMIT, "xmit", "basic xmit operation" },
+ { ATH5K_DEBUG_BEACON, "beacon", "beacon handling" },
+ { ATH5K_DEBUG_CALIBRATE, "calib", "periodic calibration" },
+ { ATH5K_DEBUG_TXPOWER, "txpower", "transmit power setting" },
+ { ATH5K_DEBUG_LED, "led", "LED management" },
+ { ATH5K_DEBUG_DUMPBANDS, "dumpbands", "dump bands" },
+ { ATH5K_DEBUG_DMA, "dma", "dma start/stop" },
+ { ATH5K_DEBUG_ANI, "ani", "adaptive noise immunity" },
+ { ATH5K_DEBUG_DESC, "desc", "descriptor chains" },
+ { ATH5K_DEBUG_ANY, "all", "show all debug levels" },
+};
+
+static ssize_t read_file_debug(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath5k_hw *ah = file->private_data;
+ char buf[700];
+ unsigned int len = 0;
+ unsigned int i;
+
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "DEBUG LEVEL: 0x%08x\n\n", ah->debug.level);
+
+ for (i = 0; i < ARRAY_SIZE(dbg_info) - 1; i++) {
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%10s %c 0x%08x - %s\n", dbg_info[i].name,
+ ah->debug.level & dbg_info[i].level ? '+' : ' ',
+ dbg_info[i].level, dbg_info[i].desc);
+ }
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%10s %c 0x%08x - %s\n", dbg_info[i].name,
+ ah->debug.level == dbg_info[i].level ? '+' : ' ',
+ dbg_info[i].level, dbg_info[i].desc);
+
+ if (len > sizeof(buf))
+ len = sizeof(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_debug(struct file *file,
+ const char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath5k_hw *ah = file->private_data;
+ unsigned int i;
+ char buf[20];
+
+ if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(dbg_info); i++) {
+ if (strncmp(buf, dbg_info[i].name,
+ strlen(dbg_info[i].name)) == 0) {
+ ah->debug.level ^= dbg_info[i].level; /* toggle bit */
+ break;
+ }
+ }
+ return count;
+}
+
+static const struct file_operations fops_debug = {
+ .read = read_file_debug,
+ .write = write_file_debug,
+ .open = ath5k_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+
+/* debugfs: antenna */
+
+static ssize_t read_file_antenna(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath5k_hw *ah = file->private_data;
+ char buf[700];
+ unsigned int len = 0;
+ unsigned int i;
+ unsigned int v;
+
+ len += snprintf(buf + len, sizeof(buf) - len, "antenna mode\t%d\n",
+ ah->ah_ant_mode);
+ len += snprintf(buf + len, sizeof(buf) - len, "default antenna\t%d\n",
+ ah->ah_def_ant);
+ len += snprintf(buf + len, sizeof(buf) - len, "tx antenna\t%d\n",
+ ah->ah_tx_ant);
+
+ len += snprintf(buf + len, sizeof(buf) - len, "\nANTENNA\t\tRX\tTX\n");
+ for (i = 1; i < ARRAY_SIZE(ah->stats.antenna_rx); i++) {
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "[antenna %d]\t%d\t%d\n",
+ i, ah->stats.antenna_rx[i], ah->stats.antenna_tx[i]);
+ }
+ len += snprintf(buf + len, sizeof(buf) - len, "[invalid]\t%d\t%d\n",
+ ah->stats.antenna_rx[0], ah->stats.antenna_tx[0]);
+
+ v = ath5k_hw_reg_read(ah, AR5K_DEFAULT_ANTENNA);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "\nAR5K_DEFAULT_ANTENNA\t0x%08x\n", v);
+
+ v = ath5k_hw_reg_read(ah, AR5K_STA_ID1);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "AR5K_STA_ID1_DEFAULT_ANTENNA\t%d\n",
+ (v & AR5K_STA_ID1_DEFAULT_ANTENNA) != 0);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "AR5K_STA_ID1_DESC_ANTENNA\t%d\n",
+ (v & AR5K_STA_ID1_DESC_ANTENNA) != 0);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "AR5K_STA_ID1_RTS_DEF_ANTENNA\t%d\n",
+ (v & AR5K_STA_ID1_RTS_DEF_ANTENNA) != 0);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "AR5K_STA_ID1_SELFGEN_DEF_ANT\t%d\n",
+ (v & AR5K_STA_ID1_SELFGEN_DEF_ANT) != 0);
+
+ v = ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "\nAR5K_PHY_AGCCTL_OFDM_DIV_DIS\t%d\n",
+ (v & AR5K_PHY_AGCCTL_OFDM_DIV_DIS) != 0);
+
+ v = ath5k_hw_reg_read(ah, AR5K_PHY_RESTART);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "AR5K_PHY_RESTART_DIV_GC\t\t%x\n",
+ (v & AR5K_PHY_RESTART_DIV_GC) >> AR5K_PHY_RESTART_DIV_GC_S);
+
+ v = ath5k_hw_reg_read(ah, AR5K_PHY_FAST_ANT_DIV);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "AR5K_PHY_FAST_ANT_DIV_EN\t%d\n",
+ (v & AR5K_PHY_FAST_ANT_DIV_EN) != 0);
+
+ v = ath5k_hw_reg_read(ah, AR5K_PHY_ANT_SWITCH_TABLE_0);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "\nAR5K_PHY_ANT_SWITCH_TABLE_0\t0x%08x\n", v);
+ v = ath5k_hw_reg_read(ah, AR5K_PHY_ANT_SWITCH_TABLE_1);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "AR5K_PHY_ANT_SWITCH_TABLE_1\t0x%08x\n", v);
+
+ if (len > sizeof(buf))
+ len = sizeof(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_antenna(struct file *file,
+ const char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath5k_hw *ah = file->private_data;
+ unsigned int i;
+ char buf[20];
+
+ if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+ return -EFAULT;
+
+ if (strncmp(buf, "diversity", 9) == 0) {
+ ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT);
+ printk(KERN_INFO "ath5k debug: enable diversity\n");
+ } else if (strncmp(buf, "fixed-a", 7) == 0) {
+ ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_A);
+ printk(KERN_INFO "ath5k debugfs: fixed antenna A\n");
+ } else if (strncmp(buf, "fixed-b", 7) == 0) {
+ ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_B);
+ printk(KERN_INFO "ath5k debug: fixed antenna B\n");
+ } else if (strncmp(buf, "clear", 5) == 0) {
+ for (i = 0; i < ARRAY_SIZE(ah->stats.antenna_rx); i++) {
+ ah->stats.antenna_rx[i] = 0;
+ ah->stats.antenna_tx[i] = 0;
+ }
+ printk(KERN_INFO "ath5k debug: cleared antenna stats\n");
+ }
+ return count;
+}
+
+static const struct file_operations fops_antenna = {
+ .read = read_file_antenna,
+ .write = write_file_antenna,
+ .open = ath5k_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+/* debugfs: misc */
+
+static ssize_t read_file_misc(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath5k_hw *ah = file->private_data;
+ char buf[700];
+ unsigned int len = 0;
+ u32 filt = ath5k_hw_get_rx_filter(ah);
+
+ len += snprintf(buf + len, sizeof(buf) - len, "bssid-mask: %pM\n",
+ ah->bssidmask);
+ len += snprintf(buf + len, sizeof(buf) - len, "filter-flags: 0x%x ",
+ filt);
+ if (filt & AR5K_RX_FILTER_UCAST)
+ len += snprintf(buf + len, sizeof(buf) - len, " UCAST");
+ if (filt & AR5K_RX_FILTER_MCAST)
+ len += snprintf(buf + len, sizeof(buf) - len, " MCAST");
+ if (filt & AR5K_RX_FILTER_BCAST)
+ len += snprintf(buf + len, sizeof(buf) - len, " BCAST");
+ if (filt & AR5K_RX_FILTER_CONTROL)
+ len += snprintf(buf + len, sizeof(buf) - len, " CONTROL");
+ if (filt & AR5K_RX_FILTER_BEACON)
+ len += snprintf(buf + len, sizeof(buf) - len, " BEACON");
+ if (filt & AR5K_RX_FILTER_PROM)
+ len += snprintf(buf + len, sizeof(buf) - len, " PROM");
+ if (filt & AR5K_RX_FILTER_XRPOLL)
+ len += snprintf(buf + len, sizeof(buf) - len, " XRPOLL");
+ if (filt & AR5K_RX_FILTER_PROBEREQ)
+ len += snprintf(buf + len, sizeof(buf) - len, " PROBEREQ");
+ if (filt & AR5K_RX_FILTER_PHYERR_5212)
+ len += snprintf(buf + len, sizeof(buf) - len, " PHYERR-5212");
+ if (filt & AR5K_RX_FILTER_RADARERR_5212)
+ len += snprintf(buf + len, sizeof(buf) - len, " RADARERR-5212");
+ if (filt & AR5K_RX_FILTER_PHYERR_5211)
+ snprintf(buf + len, sizeof(buf) - len, " PHYERR-5211");
+ if (filt & AR5K_RX_FILTER_RADARERR_5211)
+ len += snprintf(buf + len, sizeof(buf) - len, " RADARERR-5211");
+
+ len += snprintf(buf + len, sizeof(buf) - len, "\nopmode: %s (%d)\n",
+ ath_opmode_to_string(ah->opmode), ah->opmode);
+
+ if (len > sizeof(buf))
+ len = sizeof(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_misc = {
+ .read = read_file_misc,
+ .open = ath5k_debugfs_open,
+ .owner = THIS_MODULE,
+};
+
+
+/* debugfs: frameerrors */
+
+static ssize_t read_file_frameerrors(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath5k_hw *ah = file->private_data;
+ struct ath5k_statistics *st = &ah->stats;
+ char buf[700];
+ unsigned int len = 0;
+ int i;
+
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "RX\n---------------------\n");
+ len += snprintf(buf + len, sizeof(buf) - len, "CRC\t%u\t(%u%%)\n",
+ st->rxerr_crc,
+ st->rx_all_count > 0 ?
+ st->rxerr_crc * 100 / st->rx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "PHY\t%u\t(%u%%)\n",
+ st->rxerr_phy,
+ st->rx_all_count > 0 ?
+ st->rxerr_phy * 100 / st->rx_all_count : 0);
+ for (i = 0; i < 32; i++) {
+ if (st->rxerr_phy_code[i])
+ len += snprintf(buf + len, sizeof(buf) - len,
+ " phy_err[%u]\t%u\n",
+ i, st->rxerr_phy_code[i]);
+ }
+
+ len += snprintf(buf + len, sizeof(buf) - len, "FIFO\t%u\t(%u%%)\n",
+ st->rxerr_fifo,
+ st->rx_all_count > 0 ?
+ st->rxerr_fifo * 100 / st->rx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "decrypt\t%u\t(%u%%)\n",
+ st->rxerr_decrypt,
+ st->rx_all_count > 0 ?
+ st->rxerr_decrypt * 100 / st->rx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "MIC\t%u\t(%u%%)\n",
+ st->rxerr_mic,
+ st->rx_all_count > 0 ?
+ st->rxerr_mic * 100 / st->rx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "process\t%u\t(%u%%)\n",
+ st->rxerr_proc,
+ st->rx_all_count > 0 ?
+ st->rxerr_proc * 100 / st->rx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "jumbo\t%u\t(%u%%)\n",
+ st->rxerr_jumbo,
+ st->rx_all_count > 0 ?
+ st->rxerr_jumbo * 100 / st->rx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "[RX all\t%u]\n",
+ st->rx_all_count);
+ len += snprintf(buf + len, sizeof(buf) - len, "RX-all-bytes\t%u\n",
+ st->rx_bytes_count);
+
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "\nTX\n---------------------\n");
+ len += snprintf(buf + len, sizeof(buf) - len, "retry\t%u\t(%u%%)\n",
+ st->txerr_retry,
+ st->tx_all_count > 0 ?
+ st->txerr_retry * 100 / st->tx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "FIFO\t%u\t(%u%%)\n",
+ st->txerr_fifo,
+ st->tx_all_count > 0 ?
+ st->txerr_fifo * 100 / st->tx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "filter\t%u\t(%u%%)\n",
+ st->txerr_filt,
+ st->tx_all_count > 0 ?
+ st->txerr_filt * 100 / st->tx_all_count : 0);
+ len += snprintf(buf + len, sizeof(buf) - len, "[TX all\t%u]\n",
+ st->tx_all_count);
+ len += snprintf(buf + len, sizeof(buf) - len, "TX-all-bytes\t%u\n",
+ st->tx_bytes_count);
+
+ if (len > sizeof(buf))
+ len = sizeof(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_frameerrors(struct file *file,
+ const char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath5k_hw *ah = file->private_data;
+ struct ath5k_statistics *st = &ah->stats;
+ char buf[20];
+
+ if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+ return -EFAULT;
+
+ if (strncmp(buf, "clear", 5) == 0) {
+ st->rxerr_crc = 0;
+ st->rxerr_phy = 0;
+ st->rxerr_fifo = 0;
+ st->rxerr_decrypt = 0;
+ st->rxerr_mic = 0;
+ st->rxerr_proc = 0;
+ st->rxerr_jumbo = 0;
+ st->rx_all_count = 0;
+ st->txerr_retry = 0;
+ st->txerr_fifo = 0;
+ st->txerr_filt = 0;
+ st->tx_all_count = 0;
+ printk(KERN_INFO "ath5k debug: cleared frameerrors stats\n");
+ }
+ return count;
+}
+
+static const struct file_operations fops_frameerrors = {
+ .read = read_file_frameerrors,
+ .write = write_file_frameerrors,
+ .open = ath5k_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+
+/* debugfs: ani */
+
+static ssize_t read_file_ani(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath5k_hw *ah = file->private_data;
+ struct ath5k_statistics *st = &ah->stats;
+ struct ath5k_ani_state *as = &ah->ani_state;
+
+ char buf[700];
+ unsigned int len = 0;
+
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "HW has PHY error counters:\t%s\n",
+ ah->ah_capabilities.cap_has_phyerr_counters ?
+ "yes" : "no");
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "HW max spur immunity level:\t%d\n",
+ as->max_spur_level);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "\nANI state\n--------------------------------------------\n");
+ len += snprintf(buf + len, sizeof(buf) - len, "operating mode:\t\t\t");
+ switch (as->ani_mode) {
+ case ATH5K_ANI_MODE_OFF:
+ len += snprintf(buf + len, sizeof(buf) - len, "OFF\n");
+ break;
+ case ATH5K_ANI_MODE_MANUAL_LOW:
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "MANUAL LOW\n");
+ break;
+ case ATH5K_ANI_MODE_MANUAL_HIGH:
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "MANUAL HIGH\n");
+ break;
+ case ATH5K_ANI_MODE_AUTO:
+ len += snprintf(buf + len, sizeof(buf) - len, "AUTO\n");
+ break;
+ default:
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "??? (not good)\n");
+ break;
+ }
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "noise immunity level:\t\t%d\n",
+ as->noise_imm_level);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "spur immunity level:\t\t%d\n",
+ as->spur_level);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "firstep level:\t\t\t%d\n",
+ as->firstep_level);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "OFDM weak signal detection:\t%s\n",
+ as->ofdm_weak_sig ? "on" : "off");
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "CCK weak signal detection:\t%s\n",
+ as->cck_weak_sig ? "on" : "off");
+
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "\nMIB INTERRUPTS:\t\t%u\n",
+ st->mib_intr);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "beacon RSSI average:\t%d\n",
+ (int)ewma_read(&ah->ah_beacon_rssi_avg));
+
+#define CC_PRINT(_struct, _field) \
+ _struct._field, \
+ _struct.cycles > 0 ? \
+ _struct._field * 100 / _struct.cycles : 0
+
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "profcnt tx\t\t%u\t(%d%%)\n",
+ CC_PRINT(as->last_cc, tx_frame));
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "profcnt rx\t\t%u\t(%d%%)\n",
+ CC_PRINT(as->last_cc, rx_frame));
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "profcnt busy\t\t%u\t(%d%%)\n",
+ CC_PRINT(as->last_cc, rx_busy));
+#undef CC_PRINT
+ len += snprintf(buf + len, sizeof(buf) - len, "profcnt cycles\t\t%u\n",
+ as->last_cc.cycles);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "listen time\t\t%d\tlast: %d\n",
+ as->listen_time, as->last_listen);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "OFDM errors\t\t%u\tlast: %u\tsum: %u\n",
+ as->ofdm_errors, as->last_ofdm_errors,
+ as->sum_ofdm_errors);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "CCK errors\t\t%u\tlast: %u\tsum: %u\n",
+ as->cck_errors, as->last_cck_errors,
+ as->sum_cck_errors);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "AR5K_PHYERR_CNT1\t%x\t(=%d)\n",
+ ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1),
+ ATH5K_ANI_OFDM_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX -
+ ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1)));
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "AR5K_PHYERR_CNT2\t%x\t(=%d)\n",
+ ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2),
+ ATH5K_ANI_CCK_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX -
+ ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2)));
+
+ if (len > sizeof(buf))
+ len = sizeof(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_ani(struct file *file,
+ const char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath5k_hw *ah = file->private_data;
+ char buf[20];
+
+ if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+ return -EFAULT;
+
+ if (strncmp(buf, "sens-low", 8) == 0) {
+ ath5k_ani_init(ah, ATH5K_ANI_MODE_MANUAL_HIGH);
+ } else if (strncmp(buf, "sens-high", 9) == 0) {
+ ath5k_ani_init(ah, ATH5K_ANI_MODE_MANUAL_LOW);
+ } else if (strncmp(buf, "ani-off", 7) == 0) {
+ ath5k_ani_init(ah, ATH5K_ANI_MODE_OFF);
+ } else if (strncmp(buf, "ani-on", 6) == 0) {
+ ath5k_ani_init(ah, ATH5K_ANI_MODE_AUTO);
+ } else if (strncmp(buf, "noise-low", 9) == 0) {
+ ath5k_ani_set_noise_immunity_level(ah, 0);
+ } else if (strncmp(buf, "noise-high", 10) == 0) {
+ ath5k_ani_set_noise_immunity_level(ah,
+ ATH5K_ANI_MAX_NOISE_IMM_LVL);
+ } else if (strncmp(buf, "spur-low", 8) == 0) {
+ ath5k_ani_set_spur_immunity_level(ah, 0);
+ } else if (strncmp(buf, "spur-high", 9) == 0) {
+ ath5k_ani_set_spur_immunity_level(ah,
+ ah->ani_state.max_spur_level);
+ } else if (strncmp(buf, "fir-low", 7) == 0) {
+ ath5k_ani_set_firstep_level(ah, 0);
+ } else if (strncmp(buf, "fir-high", 8) == 0) {
+ ath5k_ani_set_firstep_level(ah, ATH5K_ANI_MAX_FIRSTEP_LVL);
+ } else if (strncmp(buf, "ofdm-off", 8) == 0) {
+ ath5k_ani_set_ofdm_weak_signal_detection(ah, false);
+ } else if (strncmp(buf, "ofdm-on", 7) == 0) {
+ ath5k_ani_set_ofdm_weak_signal_detection(ah, true);
+ } else if (strncmp(buf, "cck-off", 7) == 0) {
+ ath5k_ani_set_cck_weak_signal_detection(ah, false);
+ } else if (strncmp(buf, "cck-on", 6) == 0) {
+ ath5k_ani_set_cck_weak_signal_detection(ah, true);
+ }
+ return count;
+}
+
+static const struct file_operations fops_ani = {
+ .read = read_file_ani,
+ .write = write_file_ani,
+ .open = ath5k_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+
+/* debugfs: queues etc */
+
+static ssize_t read_file_queue(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath5k_hw *ah = file->private_data;
+ char buf[700];
+ unsigned int len = 0;
+
+ struct ath5k_txq *txq;
+ struct ath5k_buf *bf, *bf0;
+ int i, n;
+
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "available txbuffers: %d\n", ah->txbuf_len);
+
+ for (i = 0; i < ARRAY_SIZE(ah->txqs); i++) {
+ txq = &ah->txqs[i];
+
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%02d: %ssetup\n", i, txq->setup ? "" : "not ");
+
+ if (!txq->setup)
+ continue;
+
+ n = 0;
+ spin_lock_bh(&txq->lock);
+ list_for_each_entry_safe(bf, bf0, &txq->q, list)
+ n++;
+ spin_unlock_bh(&txq->lock);
+
+ len += snprintf(buf + len, sizeof(buf) - len,
+ " len: %d bufs: %d\n", txq->txq_len, n);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ " stuck: %d\n", txq->txq_stuck);
+ }
+
+ if (len > sizeof(buf))
+ len = sizeof(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_queue(struct file *file,
+ const char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath5k_hw *ah = file->private_data;
+ char buf[20];
+
+ if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+ return -EFAULT;
+
+ if (strncmp(buf, "start", 5) == 0)
+ ieee80211_wake_queues(ah->hw);
+ else if (strncmp(buf, "stop", 4) == 0)
+ ieee80211_stop_queues(ah->hw);
+
+ return count;
+}
+
+
+static const struct file_operations fops_queue = {
+ .read = read_file_queue,
+ .write = write_file_queue,
+ .open = ath5k_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+
+void
+ath5k_debug_init_device(struct ath5k_hw *ah)
+{
+ struct dentry *phydir;
+
+ ah->debug.level = ath5k_debug;
+
+ phydir = debugfs_create_dir("ath5k", ah->hw->wiphy->debugfsdir);
+ if (!phydir)
+ return;
+
+ debugfs_create_file("debug", S_IWUSR | S_IRUSR, phydir, ah,
+ &fops_debug);
+
+ debugfs_create_file("registers", S_IRUSR, phydir, ah, &fops_registers);
+
+ debugfs_create_file("beacon", S_IWUSR | S_IRUSR, phydir, ah,
+ &fops_beacon);
+
+ debugfs_create_file("reset", S_IWUSR, phydir, ah, &fops_reset);
+
+ debugfs_create_file("antenna", S_IWUSR | S_IRUSR, phydir, ah,
+ &fops_antenna);
+
+ debugfs_create_file("misc", S_IRUSR, phydir, ah, &fops_misc);
+
+ debugfs_create_file("frameerrors", S_IWUSR | S_IRUSR, phydir, ah,
+ &fops_frameerrors);
+
+ debugfs_create_file("ani", S_IWUSR | S_IRUSR, phydir, ah, &fops_ani);
+
+ debugfs_create_file("queue", S_IWUSR | S_IRUSR, phydir, ah,
+ &fops_queue);
+
+ debugfs_create_bool("32khz_clock", S_IWUSR | S_IRUSR, phydir,
+ &ah->ah_use_32khz_clock);
+}
+
+/* functions used in other places */
+
+void
+ath5k_debug_dump_bands(struct ath5k_hw *ah)
+{
+ unsigned int b, i;
+
+ if (likely(!(ah->debug.level & ATH5K_DEBUG_DUMPBANDS)))
+ return;
+
+ BUG_ON(!ah->sbands);
+
+ for (b = 0; b < IEEE80211_NUM_BANDS; b++) {
+ struct ieee80211_supported_band *band = &ah->sbands[b];
+ char bname[6];
+ switch (band->band) {
+ case IEEE80211_BAND_2GHZ:
+ strcpy(bname, "2 GHz");
+ break;
+ case IEEE80211_BAND_5GHZ:
+ strcpy(bname, "5 GHz");
+ break;
+ default:
+ printk(KERN_DEBUG "Band not supported: %d\n",
+ band->band);
+ return;
+ }
+ printk(KERN_DEBUG "Band %s: channels %d, rates %d\n", bname,
+ band->n_channels, band->n_bitrates);
+ printk(KERN_DEBUG " channels:\n");
+ for (i = 0; i < band->n_channels; i++)
+ printk(KERN_DEBUG " %3d %d %.4x %.4x\n",
+ ieee80211_frequency_to_channel(
+ band->channels[i].center_freq),
+ band->channels[i].center_freq,
+ band->channels[i].hw_value,
+ band->channels[i].flags);
+ printk(KERN_DEBUG " rates:\n");
+ for (i = 0; i < band->n_bitrates; i++)
+ printk(KERN_DEBUG " %4d %.4x %.4x %.4x\n",
+ band->bitrates[i].bitrate,
+ band->bitrates[i].hw_value,
+ band->bitrates[i].flags,
+ band->bitrates[i].hw_value_short);
+ }
+}
+
+static inline void
+ath5k_debug_printrxbuf(struct ath5k_buf *bf, int done,
+ struct ath5k_rx_status *rs)
+{
+ struct ath5k_desc *ds = bf->desc;
+ struct ath5k_hw_all_rx_desc *rd = &ds->ud.ds_rx;
+
+ printk(KERN_DEBUG "R (%p %llx) %08x %08x %08x %08x %08x %08x %c\n",
+ ds, (unsigned long long)bf->daddr,
+ ds->ds_link, ds->ds_data,
+ rd->rx_ctl.rx_control_0, rd->rx_ctl.rx_control_1,
+ rd->rx_stat.rx_status_0, rd->rx_stat.rx_status_1,
+ !done ? ' ' : (rs->rs_status == 0) ? '*' : '!');
+}
+
+void
+ath5k_debug_printrxbuffs(struct ath5k_hw *ah)
+{
+ struct ath5k_desc *ds;
+ struct ath5k_buf *bf;
+ struct ath5k_rx_status rs = {};
+ int status;
+
+ if (likely(!(ah->debug.level & ATH5K_DEBUG_DESC)))
+ return;
+
+ printk(KERN_DEBUG "rxdp %x, rxlink %p\n",
+ ath5k_hw_get_rxdp(ah), ah->rxlink);
+
+ spin_lock_bh(&ah->rxbuflock);
+ list_for_each_entry(bf, &ah->rxbuf, list) {
+ ds = bf->desc;
+ status = ah->ah_proc_rx_desc(ah, ds, &rs);
+ if (!status)
+ ath5k_debug_printrxbuf(bf, status == 0, &rs);
+ }
+ spin_unlock_bh(&ah->rxbuflock);
+}
+
+void
+ath5k_debug_printtxbuf(struct ath5k_hw *ah, struct ath5k_buf *bf)
+{
+ struct ath5k_desc *ds = bf->desc;
+ struct ath5k_hw_5212_tx_desc *td = &ds->ud.ds_tx5212;
+ struct ath5k_tx_status ts = {};
+ int done;
+
+ if (likely(!(ah->debug.level & ATH5K_DEBUG_DESC)))
+ return;
+
+ done = ah->ah_proc_tx_desc(ah, bf->desc, &ts);
+
+ printk(KERN_DEBUG "T (%p %llx) %08x %08x %08x %08x %08x %08x %08x "
+ "%08x %c\n", ds, (unsigned long long)bf->daddr, ds->ds_link,
+ ds->ds_data, td->tx_ctl.tx_control_0, td->tx_ctl.tx_control_1,
+ td->tx_ctl.tx_control_2, td->tx_ctl.tx_control_3,
+ td->tx_stat.tx_status_0, td->tx_stat.tx_status_1,
+ done ? ' ' : (ts.ts_status == 0) ? '*' : '!');
+}
diff --git a/drivers/net/wireless/ath/ath5k/debug.h b/drivers/net/wireless/ath/ath5k/debug.h
new file mode 100644
index 0000000..0a3f916
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/debug.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2007 Bruno Randolf <bruno@thinktube.com>
+ *
+ * This file is free software: you may copy, redistribute 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 file 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 <http://www.gnu.org/licenses/>.
+ *
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2005 Atheros Communications, Inc.
+ * Copyright (c) 2006 Devicescape Software, Inc.
+ * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
+ * Copyright (c) 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * 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 NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ */
+
+#ifndef _ATH5K_DEBUG_H
+#define _ATH5K_DEBUG_H
+
+struct ath5k_hw;
+struct sk_buff;
+struct ath5k_buf;
+
+struct ath5k_dbg_info {
+ unsigned int level; /* debug level */
+};
+
+/**
+ * enum ath5k_debug_level - ath5k debug level
+ *
+ * @ATH5K_DEBUG_RESET: reset processing
+ * @ATH5K_DEBUG_INTR: interrupt handling
+ * @ATH5K_DEBUG_MODE: mode init/setup
+ * @ATH5K_DEBUG_XMIT: basic xmit operation
+ * @ATH5K_DEBUG_BEACON: beacon handling
+ * @ATH5K_DEBUG_CALIBRATE: periodic calibration
+ * @ATH5K_DEBUG_TXPOWER: transmit power setting
+ * @ATH5K_DEBUG_LED: led management
+ * @ATH5K_DEBUG_DUMP_RX: print received skb content
+ * @ATH5K_DEBUG_DUMP_TX: print transmit skb content
+ * @ATH5K_DEBUG_DUMPBANDS: dump bands
+ * @ATH5K_DEBUG_DMA: debug dma start/stop
+ * @ATH5K_DEBUG_TRACE: trace function calls
+ * @ATH5K_DEBUG_DESC: descriptor setup
+ * @ATH5K_DEBUG_ANY: show at any debug level
+ *
+ * The debug level is used to control the amount and type of debugging output
+ * we want to see. The debug level is given in calls to ATH5K_DBG to specify
+ * where the message should appear, and the user can control the debugging
+ * messages he wants to see, either by the module parameter 'debug' on module
+ * load, or dynamically by using debugfs 'ath5k/phyX/debug'. these levels can
+ * be combined together by bitwise OR.
+ */
+enum ath5k_debug_level {
+ ATH5K_DEBUG_RESET = 0x00000001,
+ ATH5K_DEBUG_INTR = 0x00000002,
+ ATH5K_DEBUG_MODE = 0x00000004,
+ ATH5K_DEBUG_XMIT = 0x00000008,
+ ATH5K_DEBUG_BEACON = 0x00000010,
+ ATH5K_DEBUG_CALIBRATE = 0x00000020,
+ ATH5K_DEBUG_TXPOWER = 0x00000040,
+ ATH5K_DEBUG_LED = 0x00000080,
+ ATH5K_DEBUG_DUMPBANDS = 0x00000400,
+ ATH5K_DEBUG_DMA = 0x00000800,
+ ATH5K_DEBUG_ANI = 0x00002000,
+ ATH5K_DEBUG_DESC = 0x00004000,
+ ATH5K_DEBUG_ANY = 0xffffffff
+};
+
+#ifdef CONFIG_ATH5K_DEBUG
+
+#define ATH5K_DBG(_sc, _m, _fmt, ...) do { \
+ if (unlikely((_sc)->debug.level & (_m) && net_ratelimit())) \
+ ATH5K_PRINTK(_sc, KERN_DEBUG, "(%s:%d): " _fmt, \
+ __func__, __LINE__, ##__VA_ARGS__); \
+ } while (0)
+
+#define ATH5K_DBG_UNLIMIT(_sc, _m, _fmt, ...) do { \
+ if (unlikely((_sc)->debug.level & (_m))) \
+ ATH5K_PRINTK(_sc, KERN_DEBUG, "(%s:%d): " _fmt, \
+ __func__, __LINE__, ##__VA_ARGS__); \
+ } while (0)
+
+void
+ath5k_debug_init_device(struct ath5k_hw *ah);
+
+void
+ath5k_debug_printrxbuffs(struct ath5k_hw *ah);
+
+void
+ath5k_debug_dump_bands(struct ath5k_hw *ah);
+
+void
+ath5k_debug_printtxbuf(struct ath5k_hw *ah, struct ath5k_buf *bf);
+
+#else /* no debugging */
+
+#include <linux/compiler.h>
+
+static inline __printf(3, 4) void
+ATH5K_DBG(struct ath5k_hw *ah, unsigned int m, const char *fmt, ...) {}
+
+static inline __printf(3, 4) void
+ATH5K_DBG_UNLIMIT(struct ath5k_hw *ah, unsigned int m, const char *fmt, ...)
+{}
+
+static inline void
+ath5k_debug_init_device(struct ath5k_hw *ah) {}
+
+static inline void
+ath5k_debug_printrxbuffs(struct ath5k_hw *ah) {}
+
+static inline void
+ath5k_debug_dump_bands(struct ath5k_hw *ah) {}
+
+static inline void
+ath5k_debug_printtxbuf(struct ath5k_hw *ah, struct ath5k_buf *bf) {}
+
+#endif /* ifdef CONFIG_ATH5K_DEBUG */
+
+#endif /* ifndef _ATH5K_DEBUG_H */
diff --git a/drivers/net/wireless/ath/ath5k/desc.c b/drivers/net/wireless/ath/ath5k/desc.c
new file mode 100644
index 0000000..7e88dda
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/desc.c
@@ -0,0 +1,665 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
+ * Copyright (c) 2007-2008 Pavel Roskin <proski@gnu.org>
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ */
+
+/******************************\
+ Hardware Descriptor Functions
+\******************************/
+
+#include "ath5k.h"
+#include "reg.h"
+#include "debug.h"
+
+
+/************************\
+* TX Control descriptors *
+\************************/
+
+/*
+ * Initialize the 2-word tx control descriptor on 5210/5211
+ */
+static int
+ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
+ unsigned int pkt_len, unsigned int hdr_len, int padsize,
+ enum ath5k_pkt_type type,
+ unsigned int tx_power, unsigned int tx_rate0, unsigned int tx_tries0,
+ unsigned int key_index, unsigned int antenna_mode, unsigned int flags,
+ unsigned int rtscts_rate, unsigned int rtscts_duration)
+{
+ u32 frame_type;
+ struct ath5k_hw_2w_tx_ctl *tx_ctl;
+ unsigned int frame_len;
+
+ tx_ctl = &desc->ud.ds_tx5210.tx_ctl;
+
+ /*
+ * Validate input
+ * - Zero retries don't make sense.
+ * - A zero rate will put the HW into a mode where it continuously sends
+ * noise on the channel, so it is important to avoid this.
+ */
+ if (unlikely(tx_tries0 == 0)) {
+ ATH5K_ERR(ah, "zero retries\n");
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ if (unlikely(tx_rate0 == 0)) {
+ ATH5K_ERR(ah, "zero rate\n");
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ /* Clear descriptor */
+ memset(&desc->ud.ds_tx5210, 0, sizeof(struct ath5k_hw_5210_tx_desc));
+
+ /* Setup control descriptor */
+
+ /* Verify and set frame length */
+
+ /* remove padding we might have added before */
+ frame_len = pkt_len - padsize + FCS_LEN;
+
+ if (frame_len & ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN)
+ return -EINVAL;
+
+ tx_ctl->tx_control_0 = frame_len & AR5K_2W_TX_DESC_CTL0_FRAME_LEN;
+
+ /* Verify and set buffer length */
+
+ /* NB: beacon's BufLen must be a multiple of 4 bytes */
+ if (type == AR5K_PKT_TYPE_BEACON)
+ pkt_len = roundup(pkt_len, 4);
+
+ if (pkt_len & ~AR5K_2W_TX_DESC_CTL1_BUF_LEN)
+ return -EINVAL;
+
+ tx_ctl->tx_control_1 = pkt_len & AR5K_2W_TX_DESC_CTL1_BUF_LEN;
+
+ /*
+ * Verify and set header length (only 5210)
+ */
+ if (ah->ah_version == AR5K_AR5210) {
+ if (hdr_len & ~AR5K_2W_TX_DESC_CTL0_HEADER_LEN_5210)
+ return -EINVAL;
+ tx_ctl->tx_control_0 |=
+ AR5K_REG_SM(hdr_len, AR5K_2W_TX_DESC_CTL0_HEADER_LEN_5210);
+ }
+
+ /*Differences between 5210-5211*/
+ if (ah->ah_version == AR5K_AR5210) {
+ switch (type) {
+ case AR5K_PKT_TYPE_BEACON:
+ case AR5K_PKT_TYPE_PROBE_RESP:
+ frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY;
+ break;
+ case AR5K_PKT_TYPE_PIFS:
+ frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS;
+ break;
+ default:
+ frame_type = type;
+ break;
+ }
+
+ tx_ctl->tx_control_0 |=
+ AR5K_REG_SM(frame_type, AR5K_2W_TX_DESC_CTL0_FRAME_TYPE_5210) |
+ AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
+
+ } else {
+ tx_ctl->tx_control_0 |=
+ AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE) |
+ AR5K_REG_SM(antenna_mode,
+ AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT);
+ tx_ctl->tx_control_1 |=
+ AR5K_REG_SM(type, AR5K_2W_TX_DESC_CTL1_FRAME_TYPE_5211);
+ }
+
+#define _TX_FLAGS(_c, _flag) \
+ if (flags & AR5K_TXDESC_##_flag) { \
+ tx_ctl->tx_control_##_c |= \
+ AR5K_2W_TX_DESC_CTL##_c##_##_flag; \
+ }
+#define _TX_FLAGS_5211(_c, _flag) \
+ if (flags & AR5K_TXDESC_##_flag) { \
+ tx_ctl->tx_control_##_c |= \
+ AR5K_2W_TX_DESC_CTL##_c##_##_flag##_5211; \
+ }
+ _TX_FLAGS(0, CLRDMASK);
+ _TX_FLAGS(0, INTREQ);
+ _TX_FLAGS(0, RTSENA);
+
+ if (ah->ah_version == AR5K_AR5211) {
+ _TX_FLAGS_5211(0, VEOL);
+ _TX_FLAGS_5211(1, NOACK);
+ }
+
+#undef _TX_FLAGS
+#undef _TX_FLAGS_5211
+
+ /*
+ * WEP crap
+ */
+ if (key_index != AR5K_TXKEYIX_INVALID) {
+ tx_ctl->tx_control_0 |=
+ AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
+ tx_ctl->tx_control_1 |=
+ AR5K_REG_SM(key_index,
+ AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX);
+ }
+
+ /*
+ * RTS/CTS Duration [5210 ?]
+ */
+ if ((ah->ah_version == AR5K_AR5210) &&
+ (flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)))
+ tx_ctl->tx_control_1 |= rtscts_duration &
+ AR5K_2W_TX_DESC_CTL1_RTS_DURATION_5210;
+
+ return 0;
+}
+
+/*
+ * Initialize the 4-word tx control descriptor on 5212
+ */
+static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
+ struct ath5k_desc *desc, unsigned int pkt_len, unsigned int hdr_len,
+ int padsize,
+ enum ath5k_pkt_type type, unsigned int tx_power, unsigned int tx_rate0,
+ unsigned int tx_tries0, unsigned int key_index,
+ unsigned int antenna_mode, unsigned int flags,
+ unsigned int rtscts_rate,
+ unsigned int rtscts_duration)
+{
+ struct ath5k_hw_4w_tx_ctl *tx_ctl;
+ unsigned int frame_len;
+
+ /*
+ * Use local variables for these to reduce load/store access on
+ * uncached memory
+ */
+ u32 txctl0 = 0, txctl1 = 0, txctl2 = 0, txctl3 = 0;
+
+ tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
+
+ /*
+ * Validate input
+ * - Zero retries don't make sense.
+ * - A zero rate will put the HW into a mode where it continuously sends
+ * noise on the channel, so it is important to avoid this.
+ */
+ if (unlikely(tx_tries0 == 0)) {
+ ATH5K_ERR(ah, "zero retries\n");
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ if (unlikely(tx_rate0 == 0)) {
+ ATH5K_ERR(ah, "zero rate\n");
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ tx_power += ah->ah_txpower.txp_offset;
+ if (tx_power > AR5K_TUNE_MAX_TXPOWER)
+ tx_power = AR5K_TUNE_MAX_TXPOWER;
+
+ /* Clear descriptor status area */
+ memset(&desc->ud.ds_tx5212.tx_stat, 0,
+ sizeof(desc->ud.ds_tx5212.tx_stat));
+
+ /* Setup control descriptor */
+
+ /* Verify and set frame length */
+
+ /* remove padding we might have added before */
+ frame_len = pkt_len - padsize + FCS_LEN;
+
+ if (frame_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN)
+ return -EINVAL;
+
+ txctl0 = frame_len & AR5K_4W_TX_DESC_CTL0_FRAME_LEN;
+
+ /* Verify and set buffer length */
+
+ /* NB: beacon's BufLen must be a multiple of 4 bytes */
+ if (type == AR5K_PKT_TYPE_BEACON)
+ pkt_len = roundup(pkt_len, 4);
+
+ if (pkt_len & ~AR5K_4W_TX_DESC_CTL1_BUF_LEN)
+ return -EINVAL;
+
+ txctl1 = pkt_len & AR5K_4W_TX_DESC_CTL1_BUF_LEN;
+
+ txctl0 |= AR5K_REG_SM(tx_power, AR5K_4W_TX_DESC_CTL0_XMIT_POWER) |
+ AR5K_REG_SM(antenna_mode, AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT);
+ txctl1 |= AR5K_REG_SM(type, AR5K_4W_TX_DESC_CTL1_FRAME_TYPE);
+ txctl2 = AR5K_REG_SM(tx_tries0, AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0);
+ txctl3 = tx_rate0 & AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
+
+#define _TX_FLAGS(_c, _flag) \
+ if (flags & AR5K_TXDESC_##_flag) { \
+ txctl##_c |= AR5K_4W_TX_DESC_CTL##_c##_##_flag; \
+ }
+
+ _TX_FLAGS(0, CLRDMASK);
+ _TX_FLAGS(0, VEOL);
+ _TX_FLAGS(0, INTREQ);
+ _TX_FLAGS(0, RTSENA);
+ _TX_FLAGS(0, CTSENA);
+ _TX_FLAGS(1, NOACK);
+
+#undef _TX_FLAGS
+
+ /*
+ * WEP crap
+ */
+ if (key_index != AR5K_TXKEYIX_INVALID) {
+ txctl0 |= AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
+ txctl1 |= AR5K_REG_SM(key_index,
+ AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_IDX);
+ }
+
+ /*
+ * RTS/CTS
+ */
+ if (flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)) {
+ if ((flags & AR5K_TXDESC_RTSENA) &&
+ (flags & AR5K_TXDESC_CTSENA))
+ return -EINVAL;
+ txctl2 |= rtscts_duration & AR5K_4W_TX_DESC_CTL2_RTS_DURATION;
+ txctl3 |= AR5K_REG_SM(rtscts_rate,
+ AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE);
+ }
+
+ tx_ctl->tx_control_0 = txctl0;
+ tx_ctl->tx_control_1 = txctl1;
+ tx_ctl->tx_control_2 = txctl2;
+ tx_ctl->tx_control_3 = txctl3;
+
+ return 0;
+}
+
+/*
+ * Initialize a 4-word multi rate retry tx control descriptor on 5212
+ */
+int
+ath5k_hw_setup_mrr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
+ unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2,
+ u_int tx_tries2, unsigned int tx_rate3, u_int tx_tries3)
+{
+ struct ath5k_hw_4w_tx_ctl *tx_ctl;
+
+ /* no mrr support for cards older than 5212 */
+ if (ah->ah_version < AR5K_AR5212)
+ return 0;
+
+ /*
+ * Rates can be 0 as long as the retry count is 0 too.
+ * A zero rate and nonzero retry count will put the HW into a mode where
+ * it continuously sends noise on the channel, so it is important to
+ * avoid this.
+ */
+ if (unlikely((tx_rate1 == 0 && tx_tries1 != 0) ||
+ (tx_rate2 == 0 && tx_tries2 != 0) ||
+ (tx_rate3 == 0 && tx_tries3 != 0))) {
+ ATH5K_ERR(ah, "zero rate\n");
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ if (ah->ah_version == AR5K_AR5212) {
+ tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
+
+#define _XTX_TRIES(_n) \
+ if (tx_tries##_n) { \
+ tx_ctl->tx_control_2 |= \
+ AR5K_REG_SM(tx_tries##_n, \
+ AR5K_4W_TX_DESC_CTL2_XMIT_TRIES##_n); \
+ tx_ctl->tx_control_3 |= \
+ AR5K_REG_SM(tx_rate##_n, \
+ AR5K_4W_TX_DESC_CTL3_XMIT_RATE##_n); \
+ }
+
+ _XTX_TRIES(1);
+ _XTX_TRIES(2);
+ _XTX_TRIES(3);
+
+#undef _XTX_TRIES
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/***********************\
+* TX Status descriptors *
+\***********************/
+
+/*
+ * Process the tx status descriptor on 5210/5211
+ */
+static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah,
+ struct ath5k_desc *desc, struct ath5k_tx_status *ts)
+{
+ struct ath5k_hw_2w_tx_ctl *tx_ctl;
+ struct ath5k_hw_tx_status *tx_status;
+
+ tx_ctl = &desc->ud.ds_tx5210.tx_ctl;
+ tx_status = &desc->ud.ds_tx5210.tx_stat;
+
+ /* No frame has been send or error */
+ if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0))
+ return -EINPROGRESS;
+
+ /*
+ * Get descriptor status
+ */
+ ts->ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
+ AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
+ ts->ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
+ AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
+ ts->ts_final_retry = AR5K_REG_MS(tx_status->tx_status_0,
+ AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
+ /*TODO: ts->ts_virtcol + test*/
+ ts->ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
+ AR5K_DESC_TX_STATUS1_SEQ_NUM);
+ ts->ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
+ AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
+ ts->ts_antenna = 1;
+ ts->ts_status = 0;
+ ts->ts_final_idx = 0;
+
+ if (!(tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK)) {
+ if (tx_status->tx_status_0 &
+ AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
+ ts->ts_status |= AR5K_TXERR_XRETRY;
+
+ if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
+ ts->ts_status |= AR5K_TXERR_FIFO;
+
+ if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED)
+ ts->ts_status |= AR5K_TXERR_FILT;
+ }
+
+ return 0;
+}
+
+/*
+ * Process a tx status descriptor on 5212
+ */
+static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah,
+ struct ath5k_desc *desc, struct ath5k_tx_status *ts)
+{
+ struct ath5k_hw_4w_tx_ctl *tx_ctl;
+ struct ath5k_hw_tx_status *tx_status;
+ u32 txstat0, txstat1;
+
+ tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
+ tx_status = &desc->ud.ds_tx5212.tx_stat;
+
+ txstat1 = ACCESS_ONCE(tx_status->tx_status_1);
+
+ /* No frame has been send or error */
+ if (unlikely(!(txstat1 & AR5K_DESC_TX_STATUS1_DONE)))
+ return -EINPROGRESS;
+
+ txstat0 = ACCESS_ONCE(tx_status->tx_status_0);
+
+ /*
+ * Get descriptor status
+ */
+ ts->ts_tstamp = AR5K_REG_MS(txstat0,
+ AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
+ ts->ts_shortretry = AR5K_REG_MS(txstat0,
+ AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
+ ts->ts_final_retry = AR5K_REG_MS(txstat0,
+ AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
+ ts->ts_seqnum = AR5K_REG_MS(txstat1,
+ AR5K_DESC_TX_STATUS1_SEQ_NUM);
+ ts->ts_rssi = AR5K_REG_MS(txstat1,
+ AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
+ ts->ts_antenna = (txstat1 &
+ AR5K_DESC_TX_STATUS1_XMIT_ANTENNA_5212) ? 2 : 1;
+ ts->ts_status = 0;
+
+ ts->ts_final_idx = AR5K_REG_MS(txstat1,
+ AR5K_DESC_TX_STATUS1_FINAL_TS_IX_5212);
+
+ /* TX error */
+ if (!(txstat0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK)) {
+ if (txstat0 & AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
+ ts->ts_status |= AR5K_TXERR_XRETRY;
+
+ if (txstat0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
+ ts->ts_status |= AR5K_TXERR_FIFO;
+
+ if (txstat0 & AR5K_DESC_TX_STATUS0_FILTERED)
+ ts->ts_status |= AR5K_TXERR_FILT;
+ }
+
+ return 0;
+}
+
+
+/****************\
+* RX Descriptors *
+\****************/
+
+/*
+ * Initialize an rx control descriptor
+ */
+int ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
+ u32 size, unsigned int flags)
+{
+ struct ath5k_hw_rx_ctl *rx_ctl;
+
+ rx_ctl = &desc->ud.ds_rx.rx_ctl;
+
+ /*
+ * Clear the descriptor
+ * If we don't clean the status descriptor,
+ * while scanning we get too many results,
+ * most of them virtual, after some secs
+ * of scanning system hangs. M.F.
+ */
+ memset(&desc->ud.ds_rx, 0, sizeof(struct ath5k_hw_all_rx_desc));
+
+ if (unlikely(size & ~AR5K_DESC_RX_CTL1_BUF_LEN))
+ return -EINVAL;
+
+ /* Setup descriptor */
+ rx_ctl->rx_control_1 = size & AR5K_DESC_RX_CTL1_BUF_LEN;
+
+ if (flags & AR5K_RXDESC_INTREQ)
+ rx_ctl->rx_control_1 |= AR5K_DESC_RX_CTL1_INTREQ;
+
+ return 0;
+}
+
+/*
+ * Process the rx status descriptor on 5210/5211
+ */
+static int ath5k_hw_proc_5210_rx_status(struct ath5k_hw *ah,
+ struct ath5k_desc *desc, struct ath5k_rx_status *rs)
+{
+ struct ath5k_hw_rx_status *rx_status;
+
+ rx_status = &desc->ud.ds_rx.rx_stat;
+
+ /* No frame received / not ready */
+ if (unlikely(!(rx_status->rx_status_1 &
+ AR5K_5210_RX_DESC_STATUS1_DONE)))
+ return -EINPROGRESS;
+
+ memset(rs, 0, sizeof(struct ath5k_rx_status));
+
+ /*
+ * Frame receive status
+ */
+ rs->rs_datalen = rx_status->rx_status_0 &
+ AR5K_5210_RX_DESC_STATUS0_DATA_LEN;
+ rs->rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
+ AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL);
+ rs->rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
+ AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE);
+ rs->rs_more = !!(rx_status->rx_status_0 &
+ AR5K_5210_RX_DESC_STATUS0_MORE);
+ /* TODO: this timestamp is 13 bit, later on we assume 15 bit!
+ * also the HAL code for 5210 says the timestamp is bits [10..22] of the
+ * TSF, and extends the timestamp here to 15 bit.
+ * we need to check on 5210...
+ */
+ rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
+ AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
+
+ if (ah->ah_version == AR5K_AR5211)
+ rs->rs_antenna = AR5K_REG_MS(rx_status->rx_status_0,
+ AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANT_5211);
+ else
+ rs->rs_antenna = (rx_status->rx_status_0 &
+ AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANT_5210)
+ ? 2 : 1;
+
+ /*
+ * Key table status
+ */
+ if (rx_status->rx_status_1 & AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID)
+ rs->rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
+ AR5K_5210_RX_DESC_STATUS1_KEY_INDEX);
+ else
+ rs->rs_keyix = AR5K_RXKEYIX_INVALID;
+
+ /*
+ * Receive/descriptor errors
+ */
+ if (!(rx_status->rx_status_1 &
+ AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK)) {
+ if (rx_status->rx_status_1 &
+ AR5K_5210_RX_DESC_STATUS1_CRC_ERROR)
+ rs->rs_status |= AR5K_RXERR_CRC;
+
+ /* only on 5210 */
+ if ((ah->ah_version == AR5K_AR5210) &&
+ (rx_status->rx_status_1 &
+ AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN_5210))
+ rs->rs_status |= AR5K_RXERR_FIFO;
+
+ if (rx_status->rx_status_1 &
+ AR5K_5210_RX_DESC_STATUS1_PHY_ERROR) {
+ rs->rs_status |= AR5K_RXERR_PHY;
+ rs->rs_phyerr = AR5K_REG_MS(rx_status->rx_status_1,
+ AR5K_5210_RX_DESC_STATUS1_PHY_ERROR);
+ }
+
+ if (rx_status->rx_status_1 &
+ AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
+ rs->rs_status |= AR5K_RXERR_DECRYPT;
+ }
+
+ return 0;
+}
+
+/*
+ * Process the rx status descriptor on 5212
+ */
+static int ath5k_hw_proc_5212_rx_status(struct ath5k_hw *ah,
+ struct ath5k_desc *desc,
+ struct ath5k_rx_status *rs)
+{
+ struct ath5k_hw_rx_status *rx_status;
+ u32 rxstat0, rxstat1;
+
+ rx_status = &desc->ud.ds_rx.rx_stat;
+ rxstat1 = ACCESS_ONCE(rx_status->rx_status_1);
+
+ /* No frame received / not ready */
+ if (unlikely(!(rxstat1 & AR5K_5212_RX_DESC_STATUS1_DONE)))
+ return -EINPROGRESS;
+
+ memset(rs, 0, sizeof(struct ath5k_rx_status));
+ rxstat0 = ACCESS_ONCE(rx_status->rx_status_0);
+
+ /*
+ * Frame receive status
+ */
+ rs->rs_datalen = rxstat0 & AR5K_5212_RX_DESC_STATUS0_DATA_LEN;
+ rs->rs_rssi = AR5K_REG_MS(rxstat0,
+ AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL);
+ rs->rs_rate = AR5K_REG_MS(rxstat0,
+ AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE);
+ rs->rs_antenna = AR5K_REG_MS(rxstat0,
+ AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA);
+ rs->rs_more = !!(rxstat0 & AR5K_5212_RX_DESC_STATUS0_MORE);
+ rs->rs_tstamp = AR5K_REG_MS(rxstat1,
+ AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
+
+ /*
+ * Key table status
+ */
+ if (rxstat1 & AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID)
+ rs->rs_keyix = AR5K_REG_MS(rxstat1,
+ AR5K_5212_RX_DESC_STATUS1_KEY_INDEX);
+ else
+ rs->rs_keyix = AR5K_RXKEYIX_INVALID;
+
+ /*
+ * Receive/descriptor errors
+ */
+ if (!(rxstat1 & AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK)) {
+ if (rxstat1 & AR5K_5212_RX_DESC_STATUS1_CRC_ERROR)
+ rs->rs_status |= AR5K_RXERR_CRC;
+
+ if (rxstat1 & AR5K_5212_RX_DESC_STATUS1_PHY_ERROR) {
+ rs->rs_status |= AR5K_RXERR_PHY;
+ rs->rs_phyerr = AR5K_REG_MS(rxstat1,
+ AR5K_5212_RX_DESC_STATUS1_PHY_ERROR_CODE);
+ if (!ah->ah_capabilities.cap_has_phyerr_counters)
+ ath5k_ani_phy_error_report(ah, rs->rs_phyerr);
+ }
+
+ if (rxstat1 & AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
+ rs->rs_status |= AR5K_RXERR_DECRYPT;
+
+ if (rxstat1 & AR5K_5212_RX_DESC_STATUS1_MIC_ERROR)
+ rs->rs_status |= AR5K_RXERR_MIC;
+ }
+ return 0;
+}
+
+
+/********\
+* Attach *
+\********/
+
+/*
+ * Init function pointers inside ath5k_hw struct
+ */
+int ath5k_hw_init_desc_functions(struct ath5k_hw *ah)
+{
+ if (ah->ah_version == AR5K_AR5212) {
+ ah->ah_setup_tx_desc = ath5k_hw_setup_4word_tx_desc;
+ ah->ah_proc_tx_desc = ath5k_hw_proc_4word_tx_status;
+ ah->ah_proc_rx_desc = ath5k_hw_proc_5212_rx_status;
+ } else if (ah->ah_version <= AR5K_AR5211) {
+ ah->ah_setup_tx_desc = ath5k_hw_setup_2word_tx_desc;
+ ah->ah_proc_tx_desc = ath5k_hw_proc_2word_tx_status;
+ ah->ah_proc_rx_desc = ath5k_hw_proc_5210_rx_status;
+ } else
+ return -ENOTSUPP;
+ return 0;
+}
diff --git a/drivers/net/wireless/ath/ath5k/desc.h b/drivers/net/wireless/ath/ath5k/desc.h
new file mode 100644
index 0000000..cfd529b
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/desc.h
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ */
+
+/*
+ * RX/TX descriptor structures
+ */
+
+/*
+ * Common hardware RX control descriptor
+ */
+struct ath5k_hw_rx_ctl {
+ u32 rx_control_0; /* RX control word 0 */
+ u32 rx_control_1; /* RX control word 1 */
+} __packed __aligned(4);
+
+/* RX control word 1 fields/flags */
+#define AR5K_DESC_RX_CTL1_BUF_LEN 0x00000fff /* data buffer length */
+#define AR5K_DESC_RX_CTL1_INTREQ 0x00002000 /* RX interrupt request */
+
+/*
+ * Common hardware RX status descriptor
+ * 5210, 5211 and 5212 differ only in the fields and flags defined below
+ */
+struct ath5k_hw_rx_status {
+ u32 rx_status_0; /* RX status word 0 */
+ u32 rx_status_1; /* RX status word 1 */
+} __packed __aligned(4);
+
+/* 5210/5211 */
+/* RX status word 0 fields/flags */
+#define AR5K_5210_RX_DESC_STATUS0_DATA_LEN 0x00000fff /* RX data length */
+#define AR5K_5210_RX_DESC_STATUS0_MORE 0x00001000 /* more desc for this frame */
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANT_5210 0x00004000 /* [5210] receive on ant 1 */
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE 0x00078000 /* reception rate */
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE_S 15
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x07f80000 /* rssi */
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 19
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANT_5211 0x38000000 /* [5211] receive antenna */
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANT_5211_S 27
+
+/* RX status word 1 fields/flags */
+#define AR5K_5210_RX_DESC_STATUS1_DONE 0x00000001 /* descriptor complete */
+#define AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002 /* reception success */
+#define AR5K_5210_RX_DESC_STATUS1_CRC_ERROR 0x00000004 /* CRC error */
+#define AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN_5210 0x00000008 /* [5210] FIFO overrun */
+#define AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000010 /* decryption CRC failure */
+#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR 0x000000e0 /* PHY error */
+#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR_S 5
+#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100 /* key index valid */
+#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX 0x00007e00 /* decryption key index */
+#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_S 9
+#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x0fff8000 /* 13 bit of TSF */
+#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 15
+#define AR5K_5210_RX_DESC_STATUS1_KEY_CACHE_MISS 0x10000000 /* key cache miss */
+
+/* 5212 */
+/* RX status word 0 fields/flags */
+#define AR5K_5212_RX_DESC_STATUS0_DATA_LEN 0x00000fff /* RX data length */
+#define AR5K_5212_RX_DESC_STATUS0_MORE 0x00001000 /* more desc for this frame */
+#define AR5K_5212_RX_DESC_STATUS0_DECOMP_CRC_ERROR 0x00002000 /* decompression CRC error */
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE 0x000f8000 /* reception rate */
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE_S 15
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x0ff00000 /* rssi */
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 20
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA 0xf0000000 /* receive antenna */
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA_S 28
+
+/* RX status word 1 fields/flags */
+#define AR5K_5212_RX_DESC_STATUS1_DONE 0x00000001 /* descriptor complete */
+#define AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002 /* frame reception success */
+#define AR5K_5212_RX_DESC_STATUS1_CRC_ERROR 0x00000004 /* CRC error */
+#define AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000008 /* decryption CRC failure */
+#define AR5K_5212_RX_DESC_STATUS1_PHY_ERROR 0x00000010 /* PHY error */
+#define AR5K_5212_RX_DESC_STATUS1_MIC_ERROR 0x00000020 /* MIC decrypt error */
+#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100 /* key index valid */
+#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX 0x0000fe00 /* decryption key index */
+#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_S 9
+#define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x7fff0000 /* first 15bit of the TSF */
+#define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 16
+#define AR5K_5212_RX_DESC_STATUS1_KEY_CACHE_MISS 0x80000000 /* key cache miss */
+#define AR5K_5212_RX_DESC_STATUS1_PHY_ERROR_CODE 0x0000ff00 /* phy error code overlays key index and valid fields */
+#define AR5K_5212_RX_DESC_STATUS1_PHY_ERROR_CODE_S 8
+
+/**
+ * enum ath5k_phy_error_code - PHY Error codes
+ */
+enum ath5k_phy_error_code {
+ AR5K_RX_PHY_ERROR_UNDERRUN = 0, /* Transmit underrun, [5210] No error */
+ AR5K_RX_PHY_ERROR_TIMING = 1, /* Timing error */
+ AR5K_RX_PHY_ERROR_PARITY = 2, /* Illegal parity */
+ AR5K_RX_PHY_ERROR_RATE = 3, /* Illegal rate */
+ AR5K_RX_PHY_ERROR_LENGTH = 4, /* Illegal length */
+ AR5K_RX_PHY_ERROR_RADAR = 5, /* Radar detect, [5210] 64 QAM rate */
+ AR5K_RX_PHY_ERROR_SERVICE = 6, /* Illegal service */
+ AR5K_RX_PHY_ERROR_TOR = 7, /* Transmit override receive */
+ /* these are specific to the 5212 */
+ AR5K_RX_PHY_ERROR_OFDM_TIMING = 17,
+ AR5K_RX_PHY_ERROR_OFDM_SIGNAL_PARITY = 18,
+ AR5K_RX_PHY_ERROR_OFDM_RATE_ILLEGAL = 19,
+ AR5K_RX_PHY_ERROR_OFDM_LENGTH_ILLEGAL = 20,
+ AR5K_RX_PHY_ERROR_OFDM_POWER_DROP = 21,
+ AR5K_RX_PHY_ERROR_OFDM_SERVICE = 22,
+ AR5K_RX_PHY_ERROR_OFDM_RESTART = 23,
+ AR5K_RX_PHY_ERROR_CCK_TIMING = 25,
+ AR5K_RX_PHY_ERROR_CCK_HEADER_CRC = 26,
+ AR5K_RX_PHY_ERROR_CCK_RATE_ILLEGAL = 27,
+ AR5K_RX_PHY_ERROR_CCK_SERVICE = 30,
+ AR5K_RX_PHY_ERROR_CCK_RESTART = 31,
+};
+
+/*
+ * 5210/5211 hardware 2-word TX control descriptor
+ */
+struct ath5k_hw_2w_tx_ctl {
+ u32 tx_control_0; /* TX control word 0 */
+ u32 tx_control_1; /* TX control word 1 */
+} __packed __aligned(4);
+
+/* TX control word 0 fields/flags */
+#define AR5K_2W_TX_DESC_CTL0_FRAME_LEN 0x00000fff /* frame length */
+#define AR5K_2W_TX_DESC_CTL0_HEADER_LEN_5210 0x0003f000 /* [5210] header length */
+#define AR5K_2W_TX_DESC_CTL0_HEADER_LEN_5210_S 12
+#define AR5K_2W_TX_DESC_CTL0_XMIT_RATE 0x003c0000 /* tx rate */
+#define AR5K_2W_TX_DESC_CTL0_XMIT_RATE_S 18
+#define AR5K_2W_TX_DESC_CTL0_RTSENA 0x00400000 /* RTS/CTS enable */
+#define AR5K_2W_TX_DESC_CTL0_LONG_PACKET_5210 0x00800000 /* [5210] long packet */
+#define AR5K_2W_TX_DESC_CTL0_VEOL_5211 0x00800000 /* [5211] virtual end-of-list */
+#define AR5K_2W_TX_DESC_CTL0_CLRDMASK 0x01000000 /* clear destination mask */
+#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210 0x02000000 /* [5210] antenna selection */
+#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211 0x1e000000 /* [5211] antenna selection */
+#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT \
+ (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210 : \
+ AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211)
+#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_S 25
+#define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE_5210 0x1c000000 /* [5210] frame type */
+#define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE_5210_S 26
+#define AR5K_2W_TX_DESC_CTL0_INTREQ 0x20000000 /* TX interrupt request */
+#define AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID 0x40000000 /* key is valid */
+
+/* TX control word 1 fields/flags */
+#define AR5K_2W_TX_DESC_CTL1_BUF_LEN 0x00000fff /* data buffer length */
+#define AR5K_2W_TX_DESC_CTL1_MORE 0x00001000 /* more desc for this frame */
+#define AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX_5210 0x0007e000 /* [5210] key table index */
+#define AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX_5211 0x000fe000 /* [5211] key table index */
+#define AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX \
+ (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX_5210 : \
+ AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX_5211)
+#define AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX_S 13
+#define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE_5211 0x00700000 /* [5211] frame type */
+#define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE_5211_S 20
+#define AR5K_2W_TX_DESC_CTL1_NOACK_5211 0x00800000 /* [5211] no ACK */
+#define AR5K_2W_TX_DESC_CTL1_RTS_DURATION_5210 0xfff80000 /* [5210] lower 13 bit of duration */
+
+/* Frame types */
+#define AR5K_AR5210_TX_DESC_FRAME_TYPE_NORMAL 0
+#define AR5K_AR5210_TX_DESC_FRAME_TYPE_ATIM 1
+#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PSPOLL 2
+#define AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY 3
+#define AR5K_AR5211_TX_DESC_FRAME_TYPE_BEACON 3
+#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS 4
+#define AR5K_AR5211_TX_DESC_FRAME_TYPE_PRESP 4
+
+/*
+ * 5212 hardware 4-word TX control descriptor
+ */
+struct ath5k_hw_4w_tx_ctl {
+ u32 tx_control_0; /* TX control word 0 */
+ u32 tx_control_1; /* TX control word 1 */
+ u32 tx_control_2; /* TX control word 2 */
+ u32 tx_control_3; /* TX control word 3 */
+} __packed __aligned(4);
+
+/* TX control word 0 fields/flags */
+#define AR5K_4W_TX_DESC_CTL0_FRAME_LEN 0x00000fff /* frame length */
+#define AR5K_4W_TX_DESC_CTL0_XMIT_POWER 0x003f0000 /* transmit power */
+#define AR5K_4W_TX_DESC_CTL0_XMIT_POWER_S 16
+#define AR5K_4W_TX_DESC_CTL0_RTSENA 0x00400000 /* RTS/CTS enable */
+#define AR5K_4W_TX_DESC_CTL0_VEOL 0x00800000 /* virtual end-of-list */
+#define AR5K_4W_TX_DESC_CTL0_CLRDMASK 0x01000000 /* clear destination mask */
+#define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT 0x1e000000 /* TX antenna selection */
+#define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT_S 25
+#define AR5K_4W_TX_DESC_CTL0_INTREQ 0x20000000 /* TX interrupt request */
+#define AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID 0x40000000 /* destination index valid */
+#define AR5K_4W_TX_DESC_CTL0_CTSENA 0x80000000 /* precede frame with CTS */
+
+/* TX control word 1 fields/flags */
+#define AR5K_4W_TX_DESC_CTL1_BUF_LEN 0x00000fff /* data buffer length */
+#define AR5K_4W_TX_DESC_CTL1_MORE 0x00001000 /* more desc for this frame */
+#define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_IDX 0x000fe000 /* destination table index */
+#define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_IDX_S 13
+#define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE 0x00f00000 /* frame type */
+#define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE_S 20
+#define AR5K_4W_TX_DESC_CTL1_NOACK 0x01000000 /* no ACK */
+#define AR5K_4W_TX_DESC_CTL1_COMP_PROC 0x06000000 /* compression processing */
+#define AR5K_4W_TX_DESC_CTL1_COMP_PROC_S 25
+#define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN 0x18000000 /* length of frame IV */
+#define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN_S 27
+#define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN 0x60000000 /* length of frame ICV */
+#define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN_S 29
+
+/* TX control word 2 fields/flags */
+#define AR5K_4W_TX_DESC_CTL2_RTS_DURATION 0x00007fff /* RTS/CTS duration */
+#define AR5K_4W_TX_DESC_CTL2_DURATION_UPD_EN 0x00008000 /* frame duration update */
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0 0x000f0000 /* series 0 max attempts */
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0_S 16
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1 0x00f00000 /* series 1 max attempts */
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1_S 20
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2 0x0f000000 /* series 2 max attempts */
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2_S 24
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3 0xf0000000 /* series 3 max attempts */
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3_S 28
+
+/* TX control word 3 fields/flags */
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE0 0x0000001f /* series 0 tx rate */
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1 0x000003e0 /* series 1 tx rate */
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1_S 5
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2 0x00007c00 /* series 2 tx rate */
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2_S 10
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3 0x000f8000 /* series 3 tx rate */
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3_S 15
+#define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE 0x01f00000 /* RTS or CTS rate */
+#define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE_S 20
+
+/*
+ * Common TX status descriptor
+ */
+struct ath5k_hw_tx_status {
+ u32 tx_status_0; /* TX status word 0 */
+ u32 tx_status_1; /* TX status word 1 */
+} __packed __aligned(4);
+
+/* TX status word 0 fields/flags */
+#define AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK 0x00000001 /* TX success */
+#define AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES 0x00000002 /* excessive retries */
+#define AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN 0x00000004 /* FIFO underrun */
+#define AR5K_DESC_TX_STATUS0_FILTERED 0x00000008 /* TX filter indication */
+/* according to the HAL sources the spec has short/long retry counts reversed.
+ * we have it reversed to the HAL sources as well, for 5210 and 5211.
+ * For 5212 these fields are defined as RTS_FAIL_COUNT and DATA_FAIL_COUNT,
+ * but used respectively as SHORT and LONG retry count in the code later. This
+ * is consistent with the definitions here... TODO: check */
+#define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT 0x000000f0 /* short retry count */
+#define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT_S 4
+#define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT 0x00000f00 /* long retry count */
+#define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT_S 8
+#define AR5K_DESC_TX_STATUS0_VIRTCOLL_CT_5211 0x0000f000 /* [5211+] virtual collision count */
+#define AR5K_DESC_TX_STATUS0_VIRTCOLL_CT_5212_S 12
+#define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP 0xffff0000 /* TX timestamp */
+#define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP_S 16
+
+/* TX status word 1 fields/flags */
+#define AR5K_DESC_TX_STATUS1_DONE 0x00000001 /* descriptor complete */
+#define AR5K_DESC_TX_STATUS1_SEQ_NUM 0x00001ffe /* TX sequence number */
+#define AR5K_DESC_TX_STATUS1_SEQ_NUM_S 1
+#define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH 0x001fe000 /* signal strength of ACK */
+#define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH_S 13
+#define AR5K_DESC_TX_STATUS1_FINAL_TS_IX_5212 0x00600000 /* [5212] final TX attempt series ix */
+#define AR5K_DESC_TX_STATUS1_FINAL_TS_IX_5212_S 21
+#define AR5K_DESC_TX_STATUS1_COMP_SUCCESS_5212 0x00800000 /* [5212] compression status */
+#define AR5K_DESC_TX_STATUS1_XMIT_ANTENNA_5212 0x01000000 /* [5212] transmit antenna */
+
+/*
+ * 5210/5211 hardware TX descriptor
+ */
+struct ath5k_hw_5210_tx_desc {
+ struct ath5k_hw_2w_tx_ctl tx_ctl;
+ struct ath5k_hw_tx_status tx_stat;
+} __packed __aligned(4);
+
+/*
+ * 5212 hardware TX descriptor
+ */
+struct ath5k_hw_5212_tx_desc {
+ struct ath5k_hw_4w_tx_ctl tx_ctl;
+ struct ath5k_hw_tx_status tx_stat;
+} __packed __aligned(4);
+
+/*
+ * Common hardware RX descriptor
+ */
+struct ath5k_hw_all_rx_desc {
+ struct ath5k_hw_rx_ctl rx_ctl;
+ struct ath5k_hw_rx_status rx_stat;
+} __packed __aligned(4);
+
+/*
+ * Atheros hardware DMA descriptor
+ * This is read and written to by the hardware
+ */
+struct ath5k_desc {
+ u32 ds_link; /* physical address of the next descriptor */
+ u32 ds_data; /* physical address of data buffer (skb) */
+
+ union {
+ struct ath5k_hw_5210_tx_desc ds_tx5210;
+ struct ath5k_hw_5212_tx_desc ds_tx5212;
+ struct ath5k_hw_all_rx_desc ds_rx;
+ } ud;
+} __packed __aligned(4);
+
+#define AR5K_RXDESC_INTREQ 0x0020
+
+#define AR5K_TXDESC_CLRDMASK 0x0001
+#define AR5K_TXDESC_NOACK 0x0002 /*[5211+]*/
+#define AR5K_TXDESC_RTSENA 0x0004
+#define AR5K_TXDESC_CTSENA 0x0008
+#define AR5K_TXDESC_INTREQ 0x0010
+#define AR5K_TXDESC_VEOL 0x0020 /*[5211+]*/
diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c
new file mode 100644
index 0000000..2481f9c
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/dma.c
@@ -0,0 +1,845 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ */
+
+/*************************************\
+* DMA and interrupt masking functions *
+\*************************************/
+
+/*
+ * dma.c - DMA and interrupt masking functions
+ *
+ * Here we setup descriptor pointers (rxdp/txdp) start/stop dma engine and
+ * handle queue setup for 5210 chipset (rest are handled on qcu.c).
+ * Also we setup interrupt mask register (IMR) and read the various interrupt
+ * status registers (ISR).
+ *
+ * TODO: Handle SISR on 5211+ and introduce a function to return the queue
+ * number that resulted the interrupt.
+ */
+
+#include "ath5k.h"
+#include "reg.h"
+#include "debug.h"
+
+
+/*********\
+* Receive *
+\*********/
+
+/**
+ * ath5k_hw_start_rx_dma - Start DMA receive
+ *
+ * @ah: The &struct ath5k_hw
+ */
+void ath5k_hw_start_rx_dma(struct ath5k_hw *ah)
+{
+ ath5k_hw_reg_write(ah, AR5K_CR_RXE, AR5K_CR);
+ ath5k_hw_reg_read(ah, AR5K_CR);
+}
+
+/**
+ * ath5k_hw_stop_rx_dma - Stop DMA receive
+ *
+ * @ah: The &struct ath5k_hw
+ */
+static int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah)
+{
+ unsigned int i;
+
+ ath5k_hw_reg_write(ah, AR5K_CR_RXD, AR5K_CR);
+
+ /*
+ * It may take some time to disable the DMA receive unit
+ */
+ for (i = 1000; i > 0 &&
+ (ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) != 0;
+ i--)
+ udelay(100);
+
+ if (!i)
+ ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
+ "failed to stop RX DMA !\n");
+
+ return i ? 0 : -EBUSY;
+}
+
+/**
+ * ath5k_hw_get_rxdp - Get RX Descriptor's address
+ *
+ * @ah: The &struct ath5k_hw
+ */
+u32 ath5k_hw_get_rxdp(struct ath5k_hw *ah)
+{
+ return ath5k_hw_reg_read(ah, AR5K_RXDP);
+}
+
+/**
+ * ath5k_hw_set_rxdp - Set RX Descriptor's address
+ *
+ * @ah: The &struct ath5k_hw
+ * @phys_addr: RX descriptor address
+ *
+ * Returns -EIO if rx is active
+ */
+int ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr)
+{
+ if (ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) {
+ ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
+ "tried to set RXDP while rx was active !\n");
+ return -EIO;
+ }
+
+ ath5k_hw_reg_write(ah, phys_addr, AR5K_RXDP);
+ return 0;
+}
+
+
+/**********\
+* Transmit *
+\**********/
+
+/**
+ * ath5k_hw_start_tx_dma - Start DMA transmit for a specific queue
+ *
+ * @ah: The &struct ath5k_hw
+ * @queue: The hw queue number
+ *
+ * Start DMA transmit for a specific queue and since 5210 doesn't have
+ * QCU/DCU, set up queue parameters for 5210 here based on queue type (one
+ * queue for normal data and one queue for beacons). For queue setup
+ * on newer chips check out qcu.c. Returns -EINVAL if queue number is out
+ * of range or if queue is already disabled.
+ *
+ * NOTE: Must be called after setting up tx control descriptor for that
+ * queue (see below).
+ */
+int ath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue)
+{
+ u32 tx_queue;
+
+ AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
+
+ /* Return if queue is declared inactive */
+ if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
+ return -EINVAL;
+
+ if (ah->ah_version == AR5K_AR5210) {
+ tx_queue = ath5k_hw_reg_read(ah, AR5K_CR);
+
+ /*
+ * Set the queue by type on 5210
+ */
+ switch (ah->ah_txq[queue].tqi_type) {
+ case AR5K_TX_QUEUE_DATA:
+ tx_queue |= AR5K_CR_TXE0 & ~AR5K_CR_TXD0;
+ break;
+ case AR5K_TX_QUEUE_BEACON:
+ tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1;
+ ath5k_hw_reg_write(ah, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE,
+ AR5K_BSR);
+ break;
+ case AR5K_TX_QUEUE_CAB:
+ tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1;
+ ath5k_hw_reg_write(ah, AR5K_BCR_TQ1FV | AR5K_BCR_TQ1V |
+ AR5K_BCR_BDMAE, AR5K_BSR);
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* Start queue */
+ ath5k_hw_reg_write(ah, tx_queue, AR5K_CR);
+ ath5k_hw_reg_read(ah, AR5K_CR);
+ } else {
+ /* Return if queue is disabled */
+ if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXD, queue))
+ return -EIO;
+
+ /* Start queue */
+ AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXE, queue);
+ }
+
+ return 0;
+}
+
+/**
+ * ath5k_hw_stop_tx_dma - Stop DMA transmit on a specific queue
+ *
+ * @ah: The &struct ath5k_hw
+ * @queue: The hw queue number
+ *
+ * Stop DMA transmit on a specific hw queue and drain queue so we don't
+ * have any pending frames. Returns -EBUSY if we still have pending frames,
+ * -EINVAL if queue number is out of range or inactive.
+ *
+ */
+static int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
+{
+ unsigned int i = 40;
+ u32 tx_queue, pending;
+
+ AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
+
+ /* Return if queue is declared inactive */
+ if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
+ return -EINVAL;
+
+ if (ah->ah_version == AR5K_AR5210) {
+ tx_queue = ath5k_hw_reg_read(ah, AR5K_CR);
+
+ /*
+ * Set by queue type
+ */
+ switch (ah->ah_txq[queue].tqi_type) {
+ case AR5K_TX_QUEUE_DATA:
+ tx_queue |= AR5K_CR_TXD0 & ~AR5K_CR_TXE0;
+ break;
+ case AR5K_TX_QUEUE_BEACON:
+ case AR5K_TX_QUEUE_CAB:
+ /* XXX Fix me... */
+ tx_queue |= AR5K_CR_TXD1 & ~AR5K_CR_TXD1;
+ ath5k_hw_reg_write(ah, 0, AR5K_BSR);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Stop queue */
+ ath5k_hw_reg_write(ah, tx_queue, AR5K_CR);
+ ath5k_hw_reg_read(ah, AR5K_CR);
+ } else {
+
+ /*
+ * Enable DCU early termination to quickly
+ * flush any pending frames from QCU
+ */
+ AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
+ AR5K_QCU_MISC_DCU_EARLY);
+
+ /*
+ * Schedule TX disable and wait until queue is empty
+ */
+ AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXD, queue);
+
+ /* Wait for queue to stop */
+ for (i = 1000; i > 0 &&
+ (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue) != 0);
+ i--)
+ udelay(100);
+
+ if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue))
+ ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
+ "queue %i didn't stop !\n", queue);
+
+ /* Check for pending frames */
+ i = 1000;
+ do {
+ pending = ath5k_hw_reg_read(ah,
+ AR5K_QUEUE_STATUS(queue)) &
+ AR5K_QCU_STS_FRMPENDCNT;
+ udelay(100);
+ } while (--i && pending);
+
+ /* For 2413+ order PCU to drop packets using
+ * QUIET mechanism */
+ if (ah->ah_mac_version >= (AR5K_SREV_AR2414 >> 4) &&
+ pending) {
+ /* Set periodicity and duration */
+ ath5k_hw_reg_write(ah,
+ AR5K_REG_SM(100, AR5K_QUIET_CTL2_QT_PER)|
+ AR5K_REG_SM(10, AR5K_QUIET_CTL2_QT_DUR),
+ AR5K_QUIET_CTL2);
+
+ /* Enable quiet period for current TSF */
+ ath5k_hw_reg_write(ah,
+ AR5K_QUIET_CTL1_QT_EN |
+ AR5K_REG_SM(ath5k_hw_reg_read(ah,
+ AR5K_TSF_L32_5211) >> 10,
+ AR5K_QUIET_CTL1_NEXT_QT_TSF),
+ AR5K_QUIET_CTL1);
+
+ /* Force channel idle high */
+ AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211,
+ AR5K_DIAG_SW_CHANNEL_IDLE_HIGH);
+
+ /* Wait a while and disable mechanism */
+ udelay(400);
+ AR5K_REG_DISABLE_BITS(ah, AR5K_QUIET_CTL1,
+ AR5K_QUIET_CTL1_QT_EN);
+
+ /* Re-check for pending frames */
+ i = 100;
+ do {
+ pending = ath5k_hw_reg_read(ah,
+ AR5K_QUEUE_STATUS(queue)) &
+ AR5K_QCU_STS_FRMPENDCNT;
+ udelay(100);
+ } while (--i && pending);
+
+ AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5211,
+ AR5K_DIAG_SW_CHANNEL_IDLE_HIGH);
+
+ if (pending)
+ ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
+ "quiet mechanism didn't work q:%i !\n",
+ queue);
+ }
+
+ /*
+ * Disable DCU early termination
+ */
+ AR5K_REG_DISABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
+ AR5K_QCU_MISC_DCU_EARLY);
+
+ /* Clear register */
+ ath5k_hw_reg_write(ah, 0, AR5K_QCU_TXD);
+ if (pending) {
+ ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
+ "tx dma didn't stop (q:%i, frm:%i) !\n",
+ queue, pending);
+ return -EBUSY;
+ }
+ }
+
+ /* TODO: Check for success on 5210 else return error */
+ return 0;
+}
+
+/**
+ * ath5k_hw_stop_beacon_queue - Stop beacon queue
+ *
+ * @ah The &struct ath5k_hw
+ * @queue The queue number
+ *
+ * Returns -EIO if queue didn't stop
+ */
+int ath5k_hw_stop_beacon_queue(struct ath5k_hw *ah, unsigned int queue)
+{
+ int ret;
+ ret = ath5k_hw_stop_tx_dma(ah, queue);
+ if (ret) {
+ ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
+ "beacon queue didn't stop !\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+/**
+ * ath5k_hw_get_txdp - Get TX Descriptor's address for a specific queue
+ *
+ * @ah: The &struct ath5k_hw
+ * @queue: The hw queue number
+ *
+ * Get TX descriptor's address for a specific queue. For 5210 we ignore
+ * the queue number and use tx queue type since we only have 2 queues.
+ * We use TXDP0 for normal data queue and TXDP1 for beacon queue.
+ * For newer chips with QCU/DCU we just read the corresponding TXDP register.
+ *
+ * XXX: Is TXDP read and clear ?
+ */
+u32 ath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue)
+{
+ u16 tx_reg;
+
+ AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
+
+ /*
+ * Get the transmit queue descriptor pointer from the selected queue
+ */
+ /*5210 doesn't have QCU*/
+ if (ah->ah_version == AR5K_AR5210) {
+ switch (ah->ah_txq[queue].tqi_type) {
+ case AR5K_TX_QUEUE_DATA:
+ tx_reg = AR5K_NOQCU_TXDP0;
+ break;
+ case AR5K_TX_QUEUE_BEACON:
+ case AR5K_TX_QUEUE_CAB:
+ tx_reg = AR5K_NOQCU_TXDP1;
+ break;
+ default:
+ return 0xffffffff;
+ }
+ } else {
+ tx_reg = AR5K_QUEUE_TXDP(queue);
+ }
+
+ return ath5k_hw_reg_read(ah, tx_reg);
+}
+
+/**
+ * ath5k_hw_set_txdp - Set TX Descriptor's address for a specific queue
+ *
+ * @ah: The &struct ath5k_hw
+ * @queue: The hw queue number
+ *
+ * Set TX descriptor's address for a specific queue. For 5210 we ignore
+ * the queue number and we use tx queue type since we only have 2 queues
+ * so as above we use TXDP0 for normal data queue and TXDP1 for beacon queue.
+ * For newer chips with QCU/DCU we just set the corresponding TXDP register.
+ * Returns -EINVAL if queue type is invalid for 5210 and -EIO if queue is still
+ * active.
+ */
+int ath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr)
+{
+ u16 tx_reg;
+
+ AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
+
+ /*
+ * Set the transmit queue descriptor pointer register by type
+ * on 5210
+ */
+ if (ah->ah_version == AR5K_AR5210) {
+ switch (ah->ah_txq[queue].tqi_type) {
+ case AR5K_TX_QUEUE_DATA:
+ tx_reg = AR5K_NOQCU_TXDP0;
+ break;
+ case AR5K_TX_QUEUE_BEACON:
+ case AR5K_TX_QUEUE_CAB:
+ tx_reg = AR5K_NOQCU_TXDP1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ /*
+ * Set the transmit queue descriptor pointer for
+ * the selected queue on QCU for 5211+
+ * (this won't work if the queue is still active)
+ */
+ if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue))
+ return -EIO;
+
+ tx_reg = AR5K_QUEUE_TXDP(queue);
+ }
+
+ /* Set descriptor pointer */
+ ath5k_hw_reg_write(ah, phys_addr, tx_reg);
+
+ return 0;
+}
+
+/**
+ * ath5k_hw_update_tx_triglevel - Update tx trigger level
+ *
+ * @ah: The &struct ath5k_hw
+ * @increase: Flag to force increase of trigger level
+ *
+ * This function increases/decreases the tx trigger level for the tx fifo
+ * buffer (aka FIFO threshold) that is used to indicate when PCU flushes
+ * the buffer and transmits its data. Lowering this results sending small
+ * frames more quickly but can lead to tx underruns, raising it a lot can
+ * result other problems (i think bmiss is related). Right now we start with
+ * the lowest possible (64Bytes) and if we get tx underrun we increase it using
+ * the increase flag. Returns -EIO if we have reached maximum/minimum.
+ *
+ * XXX: Link this with tx DMA size ?
+ * XXX: Use it to save interrupts ?
+ * TODO: Needs testing, i think it's related to bmiss...
+ */
+int ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase)
+{
+ u32 trigger_level, imr;
+ int ret = -EIO;
+
+ /*
+ * Disable interrupts by setting the mask
+ */
+ imr = ath5k_hw_set_imr(ah, ah->ah_imr & ~AR5K_INT_GLOBAL);
+
+ trigger_level = AR5K_REG_MS(ath5k_hw_reg_read(ah, AR5K_TXCFG),
+ AR5K_TXCFG_TXFULL);
+
+ if (!increase) {
+ if (--trigger_level < AR5K_TUNE_MIN_TX_FIFO_THRES)
+ goto done;
+ } else
+ trigger_level +=
+ ((AR5K_TUNE_MAX_TX_FIFO_THRES - trigger_level) / 2);
+
+ /*
+ * Update trigger level on success
+ */
+ if (ah->ah_version == AR5K_AR5210)
+ ath5k_hw_reg_write(ah, trigger_level, AR5K_TRIG_LVL);
+ else
+ AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
+ AR5K_TXCFG_TXFULL, trigger_level);
+
+ ret = 0;
+
+done:
+ /*
+ * Restore interrupt mask
+ */
+ ath5k_hw_set_imr(ah, imr);
+
+ return ret;
+}
+
+
+/*******************\
+* Interrupt masking *
+\*******************/
+
+/**
+ * ath5k_hw_is_intr_pending - Check if we have pending interrupts
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Check if we have pending interrupts to process. Returns 1 if we
+ * have pending interrupts and 0 if we haven't.
+ */
+bool ath5k_hw_is_intr_pending(struct ath5k_hw *ah)
+{
+ return ath5k_hw_reg_read(ah, AR5K_INTPEND) == 1 ? 1 : 0;
+}
+
+/**
+ * ath5k_hw_get_isr - Get interrupt status
+ *
+ * @ah: The @struct ath5k_hw
+ * @interrupt_mask: Driver's interrupt mask used to filter out
+ * interrupts in sw.
+ *
+ * This function is used inside our interrupt handler to determine the reason
+ * for the interrupt by reading Primary Interrupt Status Register. Returns an
+ * abstract interrupt status mask which is mostly ISR with some uncommon bits
+ * being mapped on some standard non hw-specific positions
+ * (check out &ath5k_int).
+ *
+ * NOTE: We use read-and-clear register, so after this function is called ISR
+ * is zeroed.
+ */
+int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask)
+{
+ u32 data;
+
+ /*
+ * Read interrupt status from the Interrupt Status register
+ * on 5210
+ */
+ if (ah->ah_version == AR5K_AR5210) {
+ data = ath5k_hw_reg_read(ah, AR5K_ISR);
+ if (unlikely(data == AR5K_INT_NOCARD)) {
+ *interrupt_mask = data;
+ return -ENODEV;
+ }
+ } else {
+ /*
+ * Read interrupt status from Interrupt
+ * Status Register shadow copy (Read And Clear)
+ *
+ * Note: PISR/SISR Not available on 5210
+ */
+ data = ath5k_hw_reg_read(ah, AR5K_RAC_PISR);
+ if (unlikely(data == AR5K_INT_NOCARD)) {
+ *interrupt_mask = data;
+ return -ENODEV;
+ }
+ }
+
+ /*
+ * Get abstract interrupt mask (driver-compatible)
+ */
+ *interrupt_mask = (data & AR5K_INT_COMMON) & ah->ah_imr;
+
+ if (ah->ah_version != AR5K_AR5210) {
+ u32 sisr2 = ath5k_hw_reg_read(ah, AR5K_RAC_SISR2);
+
+ /*HIU = Host Interface Unit (PCI etc)*/
+ if (unlikely(data & (AR5K_ISR_HIUERR)))
+ *interrupt_mask |= AR5K_INT_FATAL;
+
+ /*Beacon Not Ready*/
+ if (unlikely(data & (AR5K_ISR_BNR)))
+ *interrupt_mask |= AR5K_INT_BNR;
+
+ if (unlikely(sisr2 & (AR5K_SISR2_SSERR |
+ AR5K_SISR2_DPERR |
+ AR5K_SISR2_MCABT)))
+ *interrupt_mask |= AR5K_INT_FATAL;
+
+ if (data & AR5K_ISR_TIM)
+ *interrupt_mask |= AR5K_INT_TIM;
+
+ if (data & AR5K_ISR_BCNMISC) {
+ if (sisr2 & AR5K_SISR2_TIM)
+ *interrupt_mask |= AR5K_INT_TIM;
+ if (sisr2 & AR5K_SISR2_DTIM)
+ *interrupt_mask |= AR5K_INT_DTIM;
+ if (sisr2 & AR5K_SISR2_DTIM_SYNC)
+ *interrupt_mask |= AR5K_INT_DTIM_SYNC;
+ if (sisr2 & AR5K_SISR2_BCN_TIMEOUT)
+ *interrupt_mask |= AR5K_INT_BCN_TIMEOUT;
+ if (sisr2 & AR5K_SISR2_CAB_TIMEOUT)
+ *interrupt_mask |= AR5K_INT_CAB_TIMEOUT;
+ }
+
+ if (data & AR5K_ISR_RXDOPPLER)
+ *interrupt_mask |= AR5K_INT_RX_DOPPLER;
+ if (data & AR5K_ISR_QCBRORN) {
+ *interrupt_mask |= AR5K_INT_QCBRORN;
+ ah->ah_txq_isr |= AR5K_REG_MS(
+ ath5k_hw_reg_read(ah, AR5K_RAC_SISR3),
+ AR5K_SISR3_QCBRORN);
+ }
+ if (data & AR5K_ISR_QCBRURN) {
+ *interrupt_mask |= AR5K_INT_QCBRURN;
+ ah->ah_txq_isr |= AR5K_REG_MS(
+ ath5k_hw_reg_read(ah, AR5K_RAC_SISR3),
+ AR5K_SISR3_QCBRURN);
+ }
+ if (data & AR5K_ISR_QTRIG) {
+ *interrupt_mask |= AR5K_INT_QTRIG;
+ ah->ah_txq_isr |= AR5K_REG_MS(
+ ath5k_hw_reg_read(ah, AR5K_RAC_SISR4),
+ AR5K_SISR4_QTRIG);
+ }
+
+ if (data & AR5K_ISR_TXOK)
+ ah->ah_txq_isr |= AR5K_REG_MS(
+ ath5k_hw_reg_read(ah, AR5K_RAC_SISR0),
+ AR5K_SISR0_QCU_TXOK);
+
+ if (data & AR5K_ISR_TXDESC)
+ ah->ah_txq_isr |= AR5K_REG_MS(
+ ath5k_hw_reg_read(ah, AR5K_RAC_SISR0),
+ AR5K_SISR0_QCU_TXDESC);
+
+ if (data & AR5K_ISR_TXERR)
+ ah->ah_txq_isr |= AR5K_REG_MS(
+ ath5k_hw_reg_read(ah, AR5K_RAC_SISR1),
+ AR5K_SISR1_QCU_TXERR);
+
+ if (data & AR5K_ISR_TXEOL)
+ ah->ah_txq_isr |= AR5K_REG_MS(
+ ath5k_hw_reg_read(ah, AR5K_RAC_SISR1),
+ AR5K_SISR1_QCU_TXEOL);
+
+ if (data & AR5K_ISR_TXURN)
+ ah->ah_txq_isr |= AR5K_REG_MS(
+ ath5k_hw_reg_read(ah, AR5K_RAC_SISR2),
+ AR5K_SISR2_QCU_TXURN);
+ } else {
+ if (unlikely(data & (AR5K_ISR_SSERR | AR5K_ISR_MCABT
+ | AR5K_ISR_HIUERR | AR5K_ISR_DPERR)))
+ *interrupt_mask |= AR5K_INT_FATAL;
+
+ /*
+ * XXX: BMISS interrupts may occur after association.
+ * I found this on 5210 code but it needs testing. If this is
+ * true we should disable them before assoc and re-enable them
+ * after a successful assoc + some jiffies.
+ interrupt_mask &= ~AR5K_INT_BMISS;
+ */
+ }
+
+ /*
+ * In case we didn't handle anything,
+ * print the register value.
+ */
+ if (unlikely(*interrupt_mask == 0 && net_ratelimit()))
+ ATH5K_PRINTF("ISR: 0x%08x IMR: 0x%08x\n", data, ah->ah_imr);
+
+ return 0;
+}
+
+/**
+ * ath5k_hw_set_imr - Set interrupt mask
+ *
+ * @ah: The &struct ath5k_hw
+ * @new_mask: The new interrupt mask to be set
+ *
+ * Set the interrupt mask in hw to save interrupts. We do that by mapping
+ * ath5k_int bits to hw-specific bits to remove abstraction and writing
+ * Interrupt Mask Register.
+ */
+enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask)
+{
+ enum ath5k_int old_mask, int_mask;
+
+ old_mask = ah->ah_imr;
+
+ /*
+ * Disable card interrupts to prevent any race conditions
+ * (they will be re-enabled afterwards if AR5K_INT GLOBAL
+ * is set again on the new mask).
+ */
+ if (old_mask & AR5K_INT_GLOBAL) {
+ ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER);
+ ath5k_hw_reg_read(ah, AR5K_IER);
+ }
+
+ /*
+ * Add additional, chipset-dependent interrupt mask flags
+ * and write them to the IMR (interrupt mask register).
+ */
+ int_mask = new_mask & AR5K_INT_COMMON;
+
+ if (ah->ah_version != AR5K_AR5210) {
+ /* Preserve per queue TXURN interrupt mask */
+ u32 simr2 = ath5k_hw_reg_read(ah, AR5K_SIMR2)
+ & AR5K_SIMR2_QCU_TXURN;
+
+ if (new_mask & AR5K_INT_FATAL) {
+ int_mask |= AR5K_IMR_HIUERR;
+ simr2 |= (AR5K_SIMR2_MCABT | AR5K_SIMR2_SSERR
+ | AR5K_SIMR2_DPERR);
+ }
+
+ /*Beacon Not Ready*/
+ if (new_mask & AR5K_INT_BNR)
+ int_mask |= AR5K_INT_BNR;
+
+ if (new_mask & AR5K_INT_TIM)
+ int_mask |= AR5K_IMR_TIM;
+
+ if (new_mask & AR5K_INT_TIM)
+ simr2 |= AR5K_SISR2_TIM;
+ if (new_mask & AR5K_INT_DTIM)
+ simr2 |= AR5K_SISR2_DTIM;
+ if (new_mask & AR5K_INT_DTIM_SYNC)
+ simr2 |= AR5K_SISR2_DTIM_SYNC;
+ if (new_mask & AR5K_INT_BCN_TIMEOUT)
+ simr2 |= AR5K_SISR2_BCN_TIMEOUT;
+ if (new_mask & AR5K_INT_CAB_TIMEOUT)
+ simr2 |= AR5K_SISR2_CAB_TIMEOUT;
+
+ if (new_mask & AR5K_INT_RX_DOPPLER)
+ int_mask |= AR5K_IMR_RXDOPPLER;
+
+ /* Note: Per queue interrupt masks
+ * are set via ath5k_hw_reset_tx_queue() (qcu.c) */
+ ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR);
+ ath5k_hw_reg_write(ah, simr2, AR5K_SIMR2);
+
+ } else {
+ if (new_mask & AR5K_INT_FATAL)
+ int_mask |= (AR5K_IMR_SSERR | AR5K_IMR_MCABT
+ | AR5K_IMR_HIUERR | AR5K_IMR_DPERR);
+
+ ath5k_hw_reg_write(ah, int_mask, AR5K_IMR);
+ }
+
+ /* If RXNOFRM interrupt is masked disable it
+ * by setting AR5K_RXNOFRM to zero */
+ if (!(new_mask & AR5K_INT_RXNOFRM))
+ ath5k_hw_reg_write(ah, 0, AR5K_RXNOFRM);
+
+ /* Store new interrupt mask */
+ ah->ah_imr = new_mask;
+
+ /* ..re-enable interrupts if AR5K_INT_GLOBAL is set */
+ if (new_mask & AR5K_INT_GLOBAL) {
+ ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER);
+ ath5k_hw_reg_read(ah, AR5K_IER);
+ }
+
+ return old_mask;
+}
+
+
+/********************\
+ Init/Stop functions
+\********************/
+
+/**
+ * ath5k_hw_dma_init - Initialize DMA unit
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Set DMA size and pre-enable interrupts
+ * (driver handles tx/rx buffer setup and
+ * dma start/stop)
+ *
+ * XXX: Save/restore RXDP/TXDP registers ?
+ */
+void ath5k_hw_dma_init(struct ath5k_hw *ah)
+{
+ /*
+ * Set Rx/Tx DMA Configuration
+ *
+ * Set standard DMA size (128). Note that
+ * a DMA size of 512 causes rx overruns and tx errors
+ * on pci-e cards (tested on 5424 but since rx overruns
+ * also occur on 5416/5418 with madwifi we set 128
+ * for all PCI-E cards to be safe).
+ *
+ * XXX: need to check 5210 for this
+ * TODO: Check out tx trigger level, it's always 64 on dumps but I
+ * guess we can tweak it and see how it goes ;-)
+ */
+ if (ah->ah_version != AR5K_AR5210) {
+ AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
+ AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B);
+ AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG,
+ AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_128B);
+ }
+
+ /* Pre-enable interrupts on 5211/5212*/
+ if (ah->ah_version != AR5K_AR5210)
+ ath5k_hw_set_imr(ah, ah->ah_imr);
+
+}
+
+/**
+ * ath5k_hw_dma_stop - stop DMA unit
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Stop tx/rx DMA and interrupts. Returns
+ * -EBUSY if tx or rx dma failed to stop.
+ *
+ * XXX: Sometimes DMA unit hangs and we have
+ * stuck frames on tx queues, only a reset
+ * can fix that.
+ */
+int ath5k_hw_dma_stop(struct ath5k_hw *ah)
+{
+ int i, qmax, err;
+ err = 0;
+
+ /* Disable interrupts */
+ ath5k_hw_set_imr(ah, 0);
+
+ /* Stop rx dma */
+ err = ath5k_hw_stop_rx_dma(ah);
+ if (err)
+ return err;
+
+ /* Clear any pending interrupts
+ * and disable tx dma */
+ if (ah->ah_version != AR5K_AR5210) {
+ ath5k_hw_reg_write(ah, 0xffffffff, AR5K_PISR);
+ qmax = AR5K_NUM_TX_QUEUES;
+ } else {
+ /* PISR/SISR Not available on 5210 */
+ ath5k_hw_reg_read(ah, AR5K_ISR);
+ qmax = AR5K_NUM_TX_QUEUES_NOQCU;
+ }
+
+ for (i = 0; i < qmax; i++) {
+ err = ath5k_hw_stop_tx_dma(ah, i);
+ /* -EINVAL -> queue inactive */
+ if (err && err != -EINVAL)
+ return err;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c
new file mode 100644
index 0000000..cd708c1
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/eeprom.c
@@ -0,0 +1,1792 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com>
+ * Copyright (c) 2008-2009 Felix Fietkau <nbd@openwrt.org>
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ */
+
+/*************************************\
+* EEPROM access functions and helpers *
+\*************************************/
+
+#include <linux/slab.h>
+
+#include "ath5k.h"
+#include "reg.h"
+#include "debug.h"
+
+
+/******************\
+* Helper functions *
+\******************/
+
+/*
+ * Translate binary channel representation in EEPROM to frequency
+ */
+static u16 ath5k_eeprom_bin2freq(struct ath5k_eeprom_info *ee, u16 bin,
+ unsigned int mode)
+{
+ u16 val;
+
+ if (bin == AR5K_EEPROM_CHANNEL_DIS)
+ return bin;
+
+ if (mode == AR5K_EEPROM_MODE_11A) {
+ if (ee->ee_version > AR5K_EEPROM_VERSION_3_2)
+ val = (5 * bin) + 4800;
+ else
+ val = bin > 62 ? (10 * 62) + (5 * (bin - 62)) + 5100 :
+ (bin * 10) + 5100;
+ } else {
+ if (ee->ee_version > AR5K_EEPROM_VERSION_3_2)
+ val = bin + 2300;
+ else
+ val = bin + 2400;
+ }
+
+ return val;
+}
+
+
+/*********\
+* Parsers *
+\*********/
+
+/*
+ * Initialize eeprom & capabilities structs
+ */
+static int
+ath5k_eeprom_init_header(struct ath5k_hw *ah)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ u16 val;
+ u32 cksum, offset, eep_max = AR5K_EEPROM_INFO_MAX;
+
+ /*
+ * Read values from EEPROM and store them in the capability structure
+ */
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MAGIC, ee_magic);
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_PROTECT, ee_protect);
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_REG_DOMAIN, ee_regdomain);
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_VERSION, ee_version);
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_HDR, ee_header);
+
+ /* Return if we have an old EEPROM */
+ if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_0)
+ return 0;
+
+ /*
+ * Validate the checksum of the EEPROM date. There are some
+ * devices with invalid EEPROMs.
+ */
+ AR5K_EEPROM_READ(AR5K_EEPROM_SIZE_UPPER, val);
+ if (val) {
+ eep_max = (val & AR5K_EEPROM_SIZE_UPPER_MASK) <<
+ AR5K_EEPROM_SIZE_ENDLOC_SHIFT;
+ AR5K_EEPROM_READ(AR5K_EEPROM_SIZE_LOWER, val);
+ eep_max = (eep_max | val) - AR5K_EEPROM_INFO_BASE;
+
+ /*
+ * Fail safe check to prevent stupid loops due
+ * to busted EEPROMs. XXX: This value is likely too
+ * big still, waiting on a better value.
+ */
+ if (eep_max > (3 * AR5K_EEPROM_INFO_MAX)) {
+ ATH5K_ERR(ah, "Invalid max custom EEPROM size: "
+ "%d (0x%04x) max expected: %d (0x%04x)\n",
+ eep_max, eep_max,
+ 3 * AR5K_EEPROM_INFO_MAX,
+ 3 * AR5K_EEPROM_INFO_MAX);
+ return -EIO;
+ }
+ }
+
+ for (cksum = 0, offset = 0; offset < eep_max; offset++) {
+ AR5K_EEPROM_READ(AR5K_EEPROM_INFO(offset), val);
+ cksum ^= val;
+ }
+ if (cksum != AR5K_EEPROM_INFO_CKSUM) {
+ ATH5K_ERR(ah, "Invalid EEPROM "
+ "checksum: 0x%04x eep_max: 0x%04x (%s)\n",
+ cksum, eep_max,
+ eep_max == AR5K_EEPROM_INFO_MAX ?
+ "default size" : "custom size");
+ return -EIO;
+ }
+
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(ah->ah_ee_version),
+ ee_ant_gain);
+
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) {
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0);
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1);
+
+ /* XXX: Don't know which versions include these two */
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC2, ee_misc2);
+
+ if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3)
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC3, ee_misc3);
+
+ if (ee->ee_version >= AR5K_EEPROM_VERSION_5_0) {
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC4, ee_misc4);
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC5, ee_misc5);
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC6, ee_misc6);
+ }
+ }
+
+ if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_3) {
+ AR5K_EEPROM_READ(AR5K_EEPROM_OBDB0_2GHZ, val);
+ ee->ee_ob[AR5K_EEPROM_MODE_11B][0] = val & 0x7;
+ ee->ee_db[AR5K_EEPROM_MODE_11B][0] = (val >> 3) & 0x7;
+
+ AR5K_EEPROM_READ(AR5K_EEPROM_OBDB1_2GHZ, val);
+ ee->ee_ob[AR5K_EEPROM_MODE_11G][0] = val & 0x7;
+ ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7;
+ }
+
+ AR5K_EEPROM_READ(AR5K_EEPROM_IS_HB63, val);
+
+ if ((ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4)) && val)
+ ee->ee_is_hb63 = true;
+ else
+ ee->ee_is_hb63 = false;
+
+ AR5K_EEPROM_READ(AR5K_EEPROM_RFKILL, val);
+ ee->ee_rfkill_pin = (u8) AR5K_REG_MS(val, AR5K_EEPROM_RFKILL_GPIO_SEL);
+ ee->ee_rfkill_pol = val & AR5K_EEPROM_RFKILL_POLARITY ? true : false;
+
+ /* Check if PCIE_OFFSET points to PCIE_SERDES_SECTION
+ * and enable serdes programming if needed.
+ *
+ * XXX: Serdes values seem to be fixed so
+ * no need to read them here, we write them
+ * during ath5k_hw_init */
+ AR5K_EEPROM_READ(AR5K_EEPROM_PCIE_OFFSET, val);
+ ee->ee_serdes = (val == AR5K_EEPROM_PCIE_SERDES_SECTION) ?
+ true : false;
+
+ return 0;
+}
+
+
+/*
+ * Read antenna infos from eeprom
+ */
+static int ath5k_eeprom_read_ants(struct ath5k_hw *ah, u32 *offset,
+ unsigned int mode)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ u32 o = *offset;
+ u16 val;
+ int i = 0;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_switch_settling[mode] = (val >> 8) & 0x7f;
+ ee->ee_atn_tx_rx[mode] = (val >> 2) & 0x3f;
+ ee->ee_ant_control[mode][i] = (val << 4) & 0x3f;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf;
+ ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f;
+ ee->ee_ant_control[mode][i++] = val & 0x3f;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_ant_control[mode][i++] = (val >> 10) & 0x3f;
+ ee->ee_ant_control[mode][i++] = (val >> 4) & 0x3f;
+ ee->ee_ant_control[mode][i] = (val << 2) & 0x3f;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_ant_control[mode][i++] |= (val >> 14) & 0x3;
+ ee->ee_ant_control[mode][i++] = (val >> 8) & 0x3f;
+ ee->ee_ant_control[mode][i++] = (val >> 2) & 0x3f;
+ ee->ee_ant_control[mode][i] = (val << 4) & 0x3f;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf;
+ ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f;
+ ee->ee_ant_control[mode][i++] = val & 0x3f;
+
+ /* Get antenna switch tables */
+ ah->ah_ant_ctl[mode][AR5K_ANT_CTL] =
+ (ee->ee_ant_control[mode][0] << 4);
+ ah->ah_ant_ctl[mode][AR5K_ANT_SWTABLE_A] =
+ ee->ee_ant_control[mode][1] |
+ (ee->ee_ant_control[mode][2] << 6) |
+ (ee->ee_ant_control[mode][3] << 12) |
+ (ee->ee_ant_control[mode][4] << 18) |
+ (ee->ee_ant_control[mode][5] << 24);
+ ah->ah_ant_ctl[mode][AR5K_ANT_SWTABLE_B] =
+ ee->ee_ant_control[mode][6] |
+ (ee->ee_ant_control[mode][7] << 6) |
+ (ee->ee_ant_control[mode][8] << 12) |
+ (ee->ee_ant_control[mode][9] << 18) |
+ (ee->ee_ant_control[mode][10] << 24);
+
+ /* return new offset */
+ *offset = o;
+
+ return 0;
+}
+
+/*
+ * Read supported modes and some mode-specific calibration data
+ * from eeprom
+ */
+static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset,
+ unsigned int mode)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ u32 o = *offset;
+ u16 val;
+
+ ee->ee_n_piers[mode] = 0;
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff);
+ switch (mode) {
+ case AR5K_EEPROM_MODE_11A:
+ ee->ee_ob[mode][3] = (val >> 5) & 0x7;
+ ee->ee_db[mode][3] = (val >> 2) & 0x7;
+ ee->ee_ob[mode][2] = (val << 1) & 0x7;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_ob[mode][2] |= (val >> 15) & 0x1;
+ ee->ee_db[mode][2] = (val >> 12) & 0x7;
+ ee->ee_ob[mode][1] = (val >> 9) & 0x7;
+ ee->ee_db[mode][1] = (val >> 6) & 0x7;
+ ee->ee_ob[mode][0] = (val >> 3) & 0x7;
+ ee->ee_db[mode][0] = val & 0x7;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ case AR5K_EEPROM_MODE_11B:
+ ee->ee_ob[mode][1] = (val >> 4) & 0x7;
+ ee->ee_db[mode][1] = val & 0x7;
+ break;
+ }
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_tx_end2xlna_enable[mode] = (val >> 8) & 0xff;
+ ee->ee_thr_62[mode] = val & 0xff;
+
+ if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2)
+ ee->ee_thr_62[mode] = mode == AR5K_EEPROM_MODE_11A ? 15 : 28;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_tx_end2xpa_disable[mode] = (val >> 8) & 0xff;
+ ee->ee_tx_frm2xpa_enable[mode] = val & 0xff;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_pga_desired_size[mode] = (val >> 8) & 0xff;
+
+ if ((val & 0xff) & 0x80)
+ ee->ee_noise_floor_thr[mode] = -((((val & 0xff) ^ 0xff)) + 1);
+ else
+ ee->ee_noise_floor_thr[mode] = val & 0xff;
+
+ if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2)
+ ee->ee_noise_floor_thr[mode] =
+ mode == AR5K_EEPROM_MODE_11A ? -54 : -1;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_xlna_gain[mode] = (val >> 5) & 0xff;
+ ee->ee_x_gain[mode] = (val >> 1) & 0xf;
+ ee->ee_xpd[mode] = val & 0x1;
+
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0 &&
+ mode != AR5K_EEPROM_MODE_11B)
+ ee->ee_fixed_bias[mode] = (val >> 13) & 0x1;
+
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_3) {
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_false_detect[mode] = (val >> 6) & 0x7f;
+
+ if (mode == AR5K_EEPROM_MODE_11A)
+ ee->ee_xr_power[mode] = val & 0x3f;
+ else {
+ /* b_DB_11[bg] and b_OB_11[bg] */
+ ee->ee_ob[mode][0] = val & 0x7;
+ ee->ee_db[mode][0] = (val >> 3) & 0x7;
+ }
+ }
+
+ if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_4) {
+ ee->ee_i_gain[mode] = AR5K_EEPROM_I_GAIN;
+ ee->ee_cck_ofdm_power_delta = AR5K_EEPROM_CCK_OFDM_DELTA;
+ } else {
+ ee->ee_i_gain[mode] = (val >> 13) & 0x7;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_i_gain[mode] |= (val << 3) & 0x38;
+
+ if (mode == AR5K_EEPROM_MODE_11G) {
+ ee->ee_cck_ofdm_power_delta = (val >> 3) & 0xff;
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_6)
+ ee->ee_scaled_cck_delta = (val >> 11) & 0x1f;
+ }
+ }
+
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0 &&
+ mode == AR5K_EEPROM_MODE_11A) {
+ ee->ee_i_cal[mode] = (val >> 8) & 0x3f;
+ ee->ee_q_cal[mode] = (val >> 3) & 0x1f;
+ }
+
+ if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_0)
+ goto done;
+
+ /* Note: >= v5 have bg freq piers on another location
+ * so these freq piers are ignored for >= v5 (should be 0xff
+ * anyway) */
+ switch (mode) {
+ case AR5K_EEPROM_MODE_11A:
+ if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_1)
+ break;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_margin_tx_rx[mode] = val & 0x3f;
+ break;
+ case AR5K_EEPROM_MODE_11B:
+ AR5K_EEPROM_READ(o++, val);
+
+ ee->ee_pwr_cal_b[0].freq =
+ ath5k_eeprom_bin2freq(ee, val & 0xff, mode);
+ if (ee->ee_pwr_cal_b[0].freq != AR5K_EEPROM_CHANNEL_DIS)
+ ee->ee_n_piers[mode]++;
+
+ ee->ee_pwr_cal_b[1].freq =
+ ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode);
+ if (ee->ee_pwr_cal_b[1].freq != AR5K_EEPROM_CHANNEL_DIS)
+ ee->ee_n_piers[mode]++;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_pwr_cal_b[2].freq =
+ ath5k_eeprom_bin2freq(ee, val & 0xff, mode);
+ if (ee->ee_pwr_cal_b[2].freq != AR5K_EEPROM_CHANNEL_DIS)
+ ee->ee_n_piers[mode]++;
+
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1)
+ ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ AR5K_EEPROM_READ(o++, val);
+
+ ee->ee_pwr_cal_g[0].freq =
+ ath5k_eeprom_bin2freq(ee, val & 0xff, mode);
+ if (ee->ee_pwr_cal_g[0].freq != AR5K_EEPROM_CHANNEL_DIS)
+ ee->ee_n_piers[mode]++;
+
+ ee->ee_pwr_cal_g[1].freq =
+ ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode);
+ if (ee->ee_pwr_cal_g[1].freq != AR5K_EEPROM_CHANNEL_DIS)
+ ee->ee_n_piers[mode]++;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_turbo_max_power[mode] = val & 0x7f;
+ ee->ee_xr_power[mode] = (val >> 7) & 0x3f;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_pwr_cal_g[2].freq =
+ ath5k_eeprom_bin2freq(ee, val & 0xff, mode);
+ if (ee->ee_pwr_cal_g[2].freq != AR5K_EEPROM_CHANNEL_DIS)
+ ee->ee_n_piers[mode]++;
+
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1)
+ ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_i_cal[mode] = (val >> 5) & 0x3f;
+ ee->ee_q_cal[mode] = val & 0x1f;
+
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_2) {
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_cck_ofdm_gain_delta = val & 0xff;
+ }
+ break;
+ }
+
+ /*
+ * Read turbo mode information on newer EEPROM versions
+ */
+ if (ee->ee_version < AR5K_EEPROM_VERSION_5_0)
+ goto done;
+
+ switch (mode) {
+ case AR5K_EEPROM_MODE_11A:
+ ee->ee_switch_settling_turbo[mode] = (val >> 6) & 0x7f;
+
+ ee->ee_atn_tx_rx_turbo[mode] = (val >> 13) & 0x7;
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_atn_tx_rx_turbo[mode] |= (val & 0x7) << 3;
+ ee->ee_margin_tx_rx_turbo[mode] = (val >> 3) & 0x3f;
+
+ ee->ee_adc_desired_size_turbo[mode] = (val >> 9) & 0x7f;
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_adc_desired_size_turbo[mode] |= (val & 0x1) << 7;
+ ee->ee_pga_desired_size_turbo[mode] = (val >> 1) & 0xff;
+
+ if (AR5K_EEPROM_EEMAP(ee->ee_misc0) >= 2)
+ ee->ee_pd_gain_overlap = (val >> 9) & 0xf;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ ee->ee_switch_settling_turbo[mode] = (val >> 8) & 0x7f;
+
+ ee->ee_atn_tx_rx_turbo[mode] = (val >> 15) & 0x7;
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_atn_tx_rx_turbo[mode] |= (val & 0x1f) << 1;
+ ee->ee_margin_tx_rx_turbo[mode] = (val >> 5) & 0x3f;
+
+ ee->ee_adc_desired_size_turbo[mode] = (val >> 11) & 0x7f;
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_adc_desired_size_turbo[mode] |= (val & 0x7) << 5;
+ ee->ee_pga_desired_size_turbo[mode] = (val >> 3) & 0xff;
+ break;
+ }
+
+done:
+ /* return new offset */
+ *offset = o;
+
+ return 0;
+}
+
+/* Read mode-specific data (except power calibration data) */
+static int
+ath5k_eeprom_init_modes(struct ath5k_hw *ah)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ u32 mode_offset[3];
+ unsigned int mode;
+ u32 offset;
+ int ret;
+
+ /*
+ * Get values for all modes
+ */
+ mode_offset[AR5K_EEPROM_MODE_11A] = AR5K_EEPROM_MODES_11A(ah->ah_ee_version);
+ mode_offset[AR5K_EEPROM_MODE_11B] = AR5K_EEPROM_MODES_11B(ah->ah_ee_version);
+ mode_offset[AR5K_EEPROM_MODE_11G] = AR5K_EEPROM_MODES_11G(ah->ah_ee_version);
+
+ ee->ee_turbo_max_power[AR5K_EEPROM_MODE_11A] =
+ AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header);
+
+ for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) {
+ offset = mode_offset[mode];
+
+ ret = ath5k_eeprom_read_ants(ah, &offset, mode);
+ if (ret)
+ return ret;
+
+ ret = ath5k_eeprom_read_modes(ah, &offset, mode);
+ if (ret)
+ return ret;
+ }
+
+ /* override for older eeprom versions for better performance */
+ if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) {
+ ee->ee_thr_62[AR5K_EEPROM_MODE_11A] = 15;
+ ee->ee_thr_62[AR5K_EEPROM_MODE_11B] = 28;
+ ee->ee_thr_62[AR5K_EEPROM_MODE_11G] = 28;
+ }
+
+ return 0;
+}
+
+/* Read the frequency piers for each mode (mostly used on newer eeproms with 0xff
+ * frequency mask) */
+static inline int
+ath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max,
+ struct ath5k_chan_pcal_info *pc, unsigned int mode)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ int o = *offset;
+ int i = 0;
+ u8 freq1, freq2;
+ u16 val;
+
+ ee->ee_n_piers[mode] = 0;
+ while (i < max) {
+ AR5K_EEPROM_READ(o++, val);
+
+ freq1 = val & 0xff;
+ if (!freq1)
+ break;
+
+ pc[i++].freq = ath5k_eeprom_bin2freq(ee,
+ freq1, mode);
+ ee->ee_n_piers[mode]++;
+
+ freq2 = (val >> 8) & 0xff;
+ if (!freq2)
+ break;
+
+ pc[i++].freq = ath5k_eeprom_bin2freq(ee,
+ freq2, mode);
+ ee->ee_n_piers[mode]++;
+ }
+
+ /* return new offset */
+ *offset = o;
+
+ return 0;
+}
+
+/* Read frequency piers for 802.11a */
+static int
+ath5k_eeprom_init_11a_pcal_freq(struct ath5k_hw *ah, int offset)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info *pcal = ee->ee_pwr_cal_a;
+ int i;
+ u16 val;
+ u8 mask;
+
+ if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) {
+ ath5k_eeprom_read_freq_list(ah, &offset,
+ AR5K_EEPROM_N_5GHZ_CHAN, pcal,
+ AR5K_EEPROM_MODE_11A);
+ } else {
+ mask = AR5K_EEPROM_FREQ_M(ah->ah_ee_version);
+
+ AR5K_EEPROM_READ(offset++, val);
+ pcal[0].freq = (val >> 9) & mask;
+ pcal[1].freq = (val >> 2) & mask;
+ pcal[2].freq = (val << 5) & mask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ pcal[2].freq |= (val >> 11) & 0x1f;
+ pcal[3].freq = (val >> 4) & mask;
+ pcal[4].freq = (val << 3) & mask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ pcal[4].freq |= (val >> 13) & 0x7;
+ pcal[5].freq = (val >> 6) & mask;
+ pcal[6].freq = (val << 1) & mask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ pcal[6].freq |= (val >> 15) & 0x1;
+ pcal[7].freq = (val >> 8) & mask;
+ pcal[8].freq = (val >> 1) & mask;
+ pcal[9].freq = (val << 6) & mask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ pcal[9].freq |= (val >> 10) & 0x3f;
+
+ /* Fixed number of piers */
+ ee->ee_n_piers[AR5K_EEPROM_MODE_11A] = 10;
+
+ for (i = 0; i < AR5K_EEPROM_N_5GHZ_CHAN; i++) {
+ pcal[i].freq = ath5k_eeprom_bin2freq(ee,
+ pcal[i].freq, AR5K_EEPROM_MODE_11A);
+ }
+ }
+
+ return 0;
+}
+
+/* Read frequency piers for 802.11bg on eeprom versions >= 5 and eemap >= 2 */
+static inline int
+ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info *pcal;
+
+ switch (mode) {
+ case AR5K_EEPROM_MODE_11B:
+ pcal = ee->ee_pwr_cal_b;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ pcal = ee->ee_pwr_cal_g;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ath5k_eeprom_read_freq_list(ah, &offset,
+ AR5K_EEPROM_N_2GHZ_CHAN_2413, pcal,
+ mode);
+
+ return 0;
+}
+
+
+/*
+ * Read power calibration for RF5111 chips
+ *
+ * For RF5111 we have an XPD -eXternal Power Detector- curve
+ * for each calibrated channel. Each curve has 0,5dB Power steps
+ * on x axis and PCDAC steps (offsets) on y axis and looks like an
+ * exponential function. To recreate the curve we read 11 points
+ * here and interpolate later.
+ */
+
+/* Used to match PCDAC steps with power values on RF5111 chips
+ * (eeprom versions < 4). For RF5111 we have 11 pre-defined PCDAC
+ * steps that match with the power values we read from eeprom. On
+ * older eeprom versions (< 3.2) these steps are equally spaced at
+ * 10% of the pcdac curve -until the curve reaches its maximum-
+ * (11 steps from 0 to 100%) but on newer eeprom versions (>= 3.2)
+ * these 11 steps are spaced in a different way. This function returns
+ * the pcdac steps based on eeprom version and curve min/max so that we
+ * can have pcdac/pwr points.
+ */
+static inline void
+ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp)
+{
+ static const u16 intercepts3[] = {
+ 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100
+ };
+ static const u16 intercepts3_2[] = {
+ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100
+ };
+ const u16 *ip;
+ int i;
+
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_2)
+ ip = intercepts3_2;
+ else
+ ip = intercepts3;
+
+ for (i = 0; i < ARRAY_SIZE(intercepts3); i++)
+ vp[i] = (ip[i] * max + (100 - ip[i]) * min) / 100;
+}
+
+static int
+ath5k_eeprom_free_pcal_info(struct ath5k_hw *ah, int mode)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info *chinfo;
+ u8 pier, pdg;
+
+ switch (mode) {
+ case AR5K_EEPROM_MODE_11A:
+ if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
+ return 0;
+ chinfo = ee->ee_pwr_cal_a;
+ break;
+ case AR5K_EEPROM_MODE_11B:
+ if (!AR5K_EEPROM_HDR_11B(ee->ee_header))
+ return 0;
+ chinfo = ee->ee_pwr_cal_b;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
+ return 0;
+ chinfo = ee->ee_pwr_cal_g;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
+ if (!chinfo[pier].pd_curves)
+ continue;
+
+ for (pdg = 0; pdg < AR5K_EEPROM_N_PD_CURVES; pdg++) {
+ struct ath5k_pdgain_info *pd =
+ &chinfo[pier].pd_curves[pdg];
+
+ kfree(pd->pd_step);
+ kfree(pd->pd_pwr);
+ }
+
+ kfree(chinfo[pier].pd_curves);
+ }
+
+ return 0;
+}
+
+/* Convert RF5111 specific data to generic raw data
+ * used by interpolation code */
+static int
+ath5k_eeprom_convert_pcal_info_5111(struct ath5k_hw *ah, int mode,
+ struct ath5k_chan_pcal_info *chinfo)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info_rf5111 *pcinfo;
+ struct ath5k_pdgain_info *pd;
+ u8 pier, point, idx;
+ u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
+
+ /* Fill raw data for each calibration pier */
+ for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
+
+ pcinfo = &chinfo[pier].rf5111_info;
+
+ /* Allocate pd_curves for this cal pier */
+ chinfo[pier].pd_curves =
+ kcalloc(AR5K_EEPROM_N_PD_CURVES,
+ sizeof(struct ath5k_pdgain_info),
+ GFP_KERNEL);
+
+ if (!chinfo[pier].pd_curves)
+ goto err_out;
+
+ /* Only one curve for RF5111
+ * find out which one and place
+ * in pd_curves.
+ * Note: ee_x_gain is reversed here */
+ for (idx = 0; idx < AR5K_EEPROM_N_PD_CURVES; idx++) {
+
+ if (!((ee->ee_x_gain[mode] >> idx) & 0x1)) {
+ pdgain_idx[0] = idx;
+ break;
+ }
+ }
+
+ ee->ee_pd_gains[mode] = 1;
+
+ pd = &chinfo[pier].pd_curves[idx];
+
+ pd->pd_points = AR5K_EEPROM_N_PWR_POINTS_5111;
+
+ /* Allocate pd points for this curve */
+ pd->pd_step = kcalloc(AR5K_EEPROM_N_PWR_POINTS_5111,
+ sizeof(u8), GFP_KERNEL);
+ if (!pd->pd_step)
+ goto err_out;
+
+ pd->pd_pwr = kcalloc(AR5K_EEPROM_N_PWR_POINTS_5111,
+ sizeof(s16), GFP_KERNEL);
+ if (!pd->pd_pwr)
+ goto err_out;
+
+ /* Fill raw dataset
+ * (convert power to 0.25dB units
+ * for RF5112 compatibility) */
+ for (point = 0; point < pd->pd_points; point++) {
+
+ /* Absolute values */
+ pd->pd_pwr[point] = 2 * pcinfo->pwr[point];
+
+ /* Already sorted */
+ pd->pd_step[point] = pcinfo->pcdac[point];
+ }
+
+ /* Set min/max pwr */
+ chinfo[pier].min_pwr = pd->pd_pwr[0];
+ chinfo[pier].max_pwr = pd->pd_pwr[10];
+
+ }
+
+ return 0;
+
+err_out:
+ ath5k_eeprom_free_pcal_info(ah, mode);
+ return -ENOMEM;
+}
+
+/* Parse EEPROM data */
+static int
+ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info *pcal;
+ int offset, ret;
+ int i;
+ u16 val;
+
+ offset = AR5K_EEPROM_GROUPS_START(ee->ee_version);
+ switch (mode) {
+ case AR5K_EEPROM_MODE_11A:
+ if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
+ return 0;
+
+ ret = ath5k_eeprom_init_11a_pcal_freq(ah,
+ offset + AR5K_EEPROM_GROUP1_OFFSET);
+ if (ret < 0)
+ return ret;
+
+ offset += AR5K_EEPROM_GROUP2_OFFSET;
+ pcal = ee->ee_pwr_cal_a;
+ break;
+ case AR5K_EEPROM_MODE_11B:
+ if (!AR5K_EEPROM_HDR_11B(ee->ee_header) &&
+ !AR5K_EEPROM_HDR_11G(ee->ee_header))
+ return 0;
+
+ pcal = ee->ee_pwr_cal_b;
+ offset += AR5K_EEPROM_GROUP3_OFFSET;
+
+ /* fixed piers */
+ pcal[0].freq = 2412;
+ pcal[1].freq = 2447;
+ pcal[2].freq = 2484;
+ ee->ee_n_piers[mode] = 3;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
+ return 0;
+
+ pcal = ee->ee_pwr_cal_g;
+ offset += AR5K_EEPROM_GROUP4_OFFSET;
+
+ /* fixed piers */
+ pcal[0].freq = 2312;
+ pcal[1].freq = 2412;
+ pcal[2].freq = 2484;
+ ee->ee_n_piers[mode] = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ee->ee_n_piers[mode]; i++) {
+ struct ath5k_chan_pcal_info_rf5111 *cdata =
+ &pcal[i].rf5111_info;
+
+ AR5K_EEPROM_READ(offset++, val);
+ cdata->pcdac_max = ((val >> 10) & AR5K_EEPROM_PCDAC_M);
+ cdata->pcdac_min = ((val >> 4) & AR5K_EEPROM_PCDAC_M);
+ cdata->pwr[0] = ((val << 2) & AR5K_EEPROM_POWER_M);
+
+ AR5K_EEPROM_READ(offset++, val);
+ cdata->pwr[0] |= ((val >> 14) & 0x3);
+ cdata->pwr[1] = ((val >> 8) & AR5K_EEPROM_POWER_M);
+ cdata->pwr[2] = ((val >> 2) & AR5K_EEPROM_POWER_M);
+ cdata->pwr[3] = ((val << 4) & AR5K_EEPROM_POWER_M);
+
+ AR5K_EEPROM_READ(offset++, val);
+ cdata->pwr[3] |= ((val >> 12) & 0xf);
+ cdata->pwr[4] = ((val >> 6) & AR5K_EEPROM_POWER_M);
+ cdata->pwr[5] = (val & AR5K_EEPROM_POWER_M);
+
+ AR5K_EEPROM_READ(offset++, val);
+ cdata->pwr[6] = ((val >> 10) & AR5K_EEPROM_POWER_M);
+ cdata->pwr[7] = ((val >> 4) & AR5K_EEPROM_POWER_M);
+ cdata->pwr[8] = ((val << 2) & AR5K_EEPROM_POWER_M);
+
+ AR5K_EEPROM_READ(offset++, val);
+ cdata->pwr[8] |= ((val >> 14) & 0x3);
+ cdata->pwr[9] = ((val >> 8) & AR5K_EEPROM_POWER_M);
+ cdata->pwr[10] = ((val >> 2) & AR5K_EEPROM_POWER_M);
+
+ ath5k_get_pcdac_intercepts(ah, cdata->pcdac_min,
+ cdata->pcdac_max, cdata->pcdac);
+ }
+
+ return ath5k_eeprom_convert_pcal_info_5111(ah, mode, pcal);
+}
+
+
+/*
+ * Read power calibration for RF5112 chips
+ *
+ * For RF5112 we have 4 XPD -eXternal Power Detector- curves
+ * for each calibrated channel on 0, -6, -12 and -18dBm but we only
+ * use the higher (3) and the lower (0) curves. Each curve has 0.5dB
+ * power steps on x axis and PCDAC steps on y axis and looks like a
+ * linear function. To recreate the curve and pass the power values
+ * on hw, we read 4 points for xpd 0 (lower gain -> max power)
+ * and 3 points for xpd 3 (higher gain -> lower power) here and
+ * interpolate later.
+ *
+ * Note: Many vendors just use xpd 0 so xpd 3 is zeroed.
+ */
+
+/* Convert RF5112 specific data to generic raw data
+ * used by interpolation code */
+static int
+ath5k_eeprom_convert_pcal_info_5112(struct ath5k_hw *ah, int mode,
+ struct ath5k_chan_pcal_info *chinfo)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info_rf5112 *pcinfo;
+ u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
+ unsigned int pier, pdg, point;
+
+ /* Fill raw data for each calibration pier */
+ for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
+
+ pcinfo = &chinfo[pier].rf5112_info;
+
+ /* Allocate pd_curves for this cal pier */
+ chinfo[pier].pd_curves =
+ kcalloc(AR5K_EEPROM_N_PD_CURVES,
+ sizeof(struct ath5k_pdgain_info),
+ GFP_KERNEL);
+
+ if (!chinfo[pier].pd_curves)
+ goto err_out;
+
+ /* Fill pd_curves */
+ for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) {
+
+ u8 idx = pdgain_idx[pdg];
+ struct ath5k_pdgain_info *pd =
+ &chinfo[pier].pd_curves[idx];
+
+ /* Lowest gain curve (max power) */
+ if (pdg == 0) {
+ /* One more point for better accuracy */
+ pd->pd_points = AR5K_EEPROM_N_XPD0_POINTS;
+
+ /* Allocate pd points for this curve */
+ pd->pd_step = kcalloc(pd->pd_points,
+ sizeof(u8), GFP_KERNEL);
+
+ if (!pd->pd_step)
+ goto err_out;
+
+ pd->pd_pwr = kcalloc(pd->pd_points,
+ sizeof(s16), GFP_KERNEL);
+
+ if (!pd->pd_pwr)
+ goto err_out;
+
+ /* Fill raw dataset
+ * (all power levels are in 0.25dB units) */
+ pd->pd_step[0] = pcinfo->pcdac_x0[0];
+ pd->pd_pwr[0] = pcinfo->pwr_x0[0];
+
+ for (point = 1; point < pd->pd_points;
+ point++) {
+ /* Absolute values */
+ pd->pd_pwr[point] =
+ pcinfo->pwr_x0[point];
+
+ /* Deltas */
+ pd->pd_step[point] =
+ pd->pd_step[point - 1] +
+ pcinfo->pcdac_x0[point];
+ }
+
+ /* Set min power for this frequency */
+ chinfo[pier].min_pwr = pd->pd_pwr[0];
+
+ /* Highest gain curve (min power) */
+ } else if (pdg == 1) {
+
+ pd->pd_points = AR5K_EEPROM_N_XPD3_POINTS;
+
+ /* Allocate pd points for this curve */
+ pd->pd_step = kcalloc(pd->pd_points,
+ sizeof(u8), GFP_KERNEL);
+
+ if (!pd->pd_step)
+ goto err_out;
+
+ pd->pd_pwr = kcalloc(pd->pd_points,
+ sizeof(s16), GFP_KERNEL);
+
+ if (!pd->pd_pwr)
+ goto err_out;
+
+ /* Fill raw dataset
+ * (all power levels are in 0.25dB units) */
+ for (point = 0; point < pd->pd_points;
+ point++) {
+ /* Absolute values */
+ pd->pd_pwr[point] =
+ pcinfo->pwr_x3[point];
+
+ /* Fixed points */
+ pd->pd_step[point] =
+ pcinfo->pcdac_x3[point];
+ }
+
+ /* Since we have a higher gain curve
+ * override min power */
+ chinfo[pier].min_pwr = pd->pd_pwr[0];
+ }
+ }
+ }
+
+ return 0;
+
+err_out:
+ ath5k_eeprom_free_pcal_info(ah, mode);
+ return -ENOMEM;
+}
+
+/* Parse EEPROM data */
+static int
+ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info_rf5112 *chan_pcal_info;
+ struct ath5k_chan_pcal_info *gen_chan_info;
+ u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
+ u32 offset;
+ u8 i, c;
+ u16 val;
+ u8 pd_gains = 0;
+
+ /* Count how many curves we have and
+ * identify them (which one of the 4
+ * available curves we have on each count).
+ * Curves are stored from lower (x0) to
+ * higher (x3) gain */
+ for (i = 0; i < AR5K_EEPROM_N_PD_CURVES; i++) {
+ /* ee_x_gain[mode] is x gain mask */
+ if ((ee->ee_x_gain[mode] >> i) & 0x1)
+ pdgain_idx[pd_gains++] = i;
+ }
+ ee->ee_pd_gains[mode] = pd_gains;
+
+ if (pd_gains == 0 || pd_gains > 2)
+ return -EINVAL;
+
+ switch (mode) {
+ case AR5K_EEPROM_MODE_11A:
+ /*
+ * Read 5GHz EEPROM channels
+ */
+ offset = AR5K_EEPROM_GROUPS_START(ee->ee_version);
+ ath5k_eeprom_init_11a_pcal_freq(ah, offset);
+
+ offset += AR5K_EEPROM_GROUP2_OFFSET;
+ gen_chan_info = ee->ee_pwr_cal_a;
+ break;
+ case AR5K_EEPROM_MODE_11B:
+ offset = AR5K_EEPROM_GROUPS_START(ee->ee_version);
+ if (AR5K_EEPROM_HDR_11A(ee->ee_header))
+ offset += AR5K_EEPROM_GROUP3_OFFSET;
+
+ /* NB: frequency piers parsed during mode init */
+ gen_chan_info = ee->ee_pwr_cal_b;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ offset = AR5K_EEPROM_GROUPS_START(ee->ee_version);
+ if (AR5K_EEPROM_HDR_11A(ee->ee_header))
+ offset += AR5K_EEPROM_GROUP4_OFFSET;
+ else if (AR5K_EEPROM_HDR_11B(ee->ee_header))
+ offset += AR5K_EEPROM_GROUP2_OFFSET;
+
+ /* NB: frequency piers parsed during mode init */
+ gen_chan_info = ee->ee_pwr_cal_g;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ee->ee_n_piers[mode]; i++) {
+ chan_pcal_info = &gen_chan_info[i].rf5112_info;
+
+ /* Power values in quarter dB
+ * for the lower xpd gain curve
+ * (0 dBm -> higher output power) */
+ for (c = 0; c < AR5K_EEPROM_N_XPD0_POINTS; c++) {
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pwr_x0[c] = (s8) (val & 0xff);
+ chan_pcal_info->pwr_x0[++c] = (s8) ((val >> 8) & 0xff);
+ }
+
+ /* PCDAC steps
+ * corresponding to the above power
+ * measurements */
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pcdac_x0[1] = (val & 0x1f);
+ chan_pcal_info->pcdac_x0[2] = ((val >> 5) & 0x1f);
+ chan_pcal_info->pcdac_x0[3] = ((val >> 10) & 0x1f);
+
+ /* Power values in quarter dB
+ * for the higher xpd gain curve
+ * (18 dBm -> lower output power) */
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pwr_x3[0] = (s8) (val & 0xff);
+ chan_pcal_info->pwr_x3[1] = (s8) ((val >> 8) & 0xff);
+
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pwr_x3[2] = (val & 0xff);
+
+ /* PCDAC steps
+ * corresponding to the above power
+ * measurements (fixed) */
+ chan_pcal_info->pcdac_x3[0] = 20;
+ chan_pcal_info->pcdac_x3[1] = 35;
+ chan_pcal_info->pcdac_x3[2] = 63;
+
+ if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3) {
+ chan_pcal_info->pcdac_x0[0] = ((val >> 8) & 0x3f);
+
+ /* Last xpd0 power level is also channel maximum */
+ gen_chan_info[i].max_pwr = chan_pcal_info->pwr_x0[3];
+ } else {
+ chan_pcal_info->pcdac_x0[0] = 1;
+ gen_chan_info[i].max_pwr = (s8) ((val >> 8) & 0xff);
+ }
+
+ }
+
+ return ath5k_eeprom_convert_pcal_info_5112(ah, mode, gen_chan_info);
+}
+
+
+/*
+ * Read power calibration for RF2413 chips
+ *
+ * For RF2413 we have a Power to PDDAC table (Power Detector)
+ * instead of a PCDAC and 4 pd gain curves for each calibrated channel.
+ * Each curve has power on x axis in 0.5 db steps and PDDADC steps on y
+ * axis and looks like an exponential function like the RF5111 curve.
+ *
+ * To recreate the curves we read here the points and interpolate
+ * later. Note that in most cases only 2 (higher and lower) curves are
+ * used (like RF5112) but vendors have the opportunity to include all
+ * 4 curves on eeprom. The final curve (higher power) has an extra
+ * point for better accuracy like RF5112.
+ */
+
+/* For RF2413 power calibration data doesn't start on a fixed location and
+ * if a mode is not supported, its section is missing -not zeroed-.
+ * So we need to calculate the starting offset for each section by using
+ * these two functions */
+
+/* Return the size of each section based on the mode and the number of pd
+ * gains available (maximum 4). */
+static inline unsigned int
+ath5k_pdgains_size_2413(struct ath5k_eeprom_info *ee, unsigned int mode)
+{
+ static const unsigned int pdgains_size[] = { 4, 6, 9, 12 };
+ unsigned int sz;
+
+ sz = pdgains_size[ee->ee_pd_gains[mode] - 1];
+ sz *= ee->ee_n_piers[mode];
+
+ return sz;
+}
+
+/* Return the starting offset for a section based on the modes supported
+ * and each section's size. */
+static unsigned int
+ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode)
+{
+ u32 offset = AR5K_EEPROM_CAL_DATA_START(ee->ee_misc4);
+
+ switch (mode) {
+ case AR5K_EEPROM_MODE_11G:
+ if (AR5K_EEPROM_HDR_11B(ee->ee_header))
+ offset += ath5k_pdgains_size_2413(ee,
+ AR5K_EEPROM_MODE_11B) +
+ AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
+ /* fall through */
+ case AR5K_EEPROM_MODE_11B:
+ if (AR5K_EEPROM_HDR_11A(ee->ee_header))
+ offset += ath5k_pdgains_size_2413(ee,
+ AR5K_EEPROM_MODE_11A) +
+ AR5K_EEPROM_N_5GHZ_CHAN / 2;
+ /* fall through */
+ case AR5K_EEPROM_MODE_11A:
+ break;
+ default:
+ break;
+ }
+
+ return offset;
+}
+
+/* Convert RF2413 specific data to generic raw data
+ * used by interpolation code */
+static int
+ath5k_eeprom_convert_pcal_info_2413(struct ath5k_hw *ah, int mode,
+ struct ath5k_chan_pcal_info *chinfo)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info_rf2413 *pcinfo;
+ u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
+ unsigned int pier, pdg, point;
+
+ /* Fill raw data for each calibration pier */
+ for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
+
+ pcinfo = &chinfo[pier].rf2413_info;
+
+ /* Allocate pd_curves for this cal pier */
+ chinfo[pier].pd_curves =
+ kcalloc(AR5K_EEPROM_N_PD_CURVES,
+ sizeof(struct ath5k_pdgain_info),
+ GFP_KERNEL);
+
+ if (!chinfo[pier].pd_curves)
+ goto err_out;
+
+ /* Fill pd_curves */
+ for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) {
+
+ u8 idx = pdgain_idx[pdg];
+ struct ath5k_pdgain_info *pd =
+ &chinfo[pier].pd_curves[idx];
+
+ /* One more point for the highest power
+ * curve (lowest gain) */
+ if (pdg == ee->ee_pd_gains[mode] - 1)
+ pd->pd_points = AR5K_EEPROM_N_PD_POINTS;
+ else
+ pd->pd_points = AR5K_EEPROM_N_PD_POINTS - 1;
+
+ /* Allocate pd points for this curve */
+ pd->pd_step = kcalloc(pd->pd_points,
+ sizeof(u8), GFP_KERNEL);
+
+ if (!pd->pd_step)
+ goto err_out;
+
+ pd->pd_pwr = kcalloc(pd->pd_points,
+ sizeof(s16), GFP_KERNEL);
+
+ if (!pd->pd_pwr)
+ goto err_out;
+
+ /* Fill raw dataset
+ * convert all pwr levels to
+ * quarter dB for RF5112 compatibility */
+ pd->pd_step[0] = pcinfo->pddac_i[pdg];
+ pd->pd_pwr[0] = 4 * pcinfo->pwr_i[pdg];
+
+ for (point = 1; point < pd->pd_points; point++) {
+
+ pd->pd_pwr[point] = pd->pd_pwr[point - 1] +
+ 2 * pcinfo->pwr[pdg][point - 1];
+
+ pd->pd_step[point] = pd->pd_step[point - 1] +
+ pcinfo->pddac[pdg][point - 1];
+
+ }
+
+ /* Highest gain curve -> min power */
+ if (pdg == 0)
+ chinfo[pier].min_pwr = pd->pd_pwr[0];
+
+ /* Lowest gain curve -> max power */
+ if (pdg == ee->ee_pd_gains[mode] - 1)
+ chinfo[pier].max_pwr =
+ pd->pd_pwr[pd->pd_points - 1];
+ }
+ }
+
+ return 0;
+
+err_out:
+ ath5k_eeprom_free_pcal_info(ah, mode);
+ return -ENOMEM;
+}
+
+/* Parse EEPROM data */
+static int
+ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info_rf2413 *pcinfo;
+ struct ath5k_chan_pcal_info *chinfo;
+ u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
+ u32 offset;
+ int idx, i;
+ u16 val;
+ u8 pd_gains = 0;
+
+ /* Count how many curves we have and
+ * identify them (which one of the 4
+ * available curves we have on each count).
+ * Curves are stored from higher to
+ * lower gain so we go backwards */
+ for (idx = AR5K_EEPROM_N_PD_CURVES - 1; idx >= 0; idx--) {
+ /* ee_x_gain[mode] is x gain mask */
+ if ((ee->ee_x_gain[mode] >> idx) & 0x1)
+ pdgain_idx[pd_gains++] = idx;
+
+ }
+ ee->ee_pd_gains[mode] = pd_gains;
+
+ if (pd_gains == 0)
+ return -EINVAL;
+
+ offset = ath5k_cal_data_offset_2413(ee, mode);
+ switch (mode) {
+ case AR5K_EEPROM_MODE_11A:
+ if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
+ return 0;
+
+ ath5k_eeprom_init_11a_pcal_freq(ah, offset);
+ offset += AR5K_EEPROM_N_5GHZ_CHAN / 2;
+ chinfo = ee->ee_pwr_cal_a;
+ break;
+ case AR5K_EEPROM_MODE_11B:
+ if (!AR5K_EEPROM_HDR_11B(ee->ee_header))
+ return 0;
+
+ ath5k_eeprom_init_11bg_2413(ah, mode, offset);
+ offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
+ chinfo = ee->ee_pwr_cal_b;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
+ return 0;
+
+ ath5k_eeprom_init_11bg_2413(ah, mode, offset);
+ offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
+ chinfo = ee->ee_pwr_cal_g;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ee->ee_n_piers[mode]; i++) {
+ pcinfo = &chinfo[i].rf2413_info;
+
+ /*
+ * Read pwr_i, pddac_i and the first
+ * 2 pd points (pwr, pddac)
+ */
+ AR5K_EEPROM_READ(offset++, val);
+ pcinfo->pwr_i[0] = val & 0x1f;
+ pcinfo->pddac_i[0] = (val >> 5) & 0x7f;
+ pcinfo->pwr[0][0] = (val >> 12) & 0xf;
+
+ AR5K_EEPROM_READ(offset++, val);
+ pcinfo->pddac[0][0] = val & 0x3f;
+ pcinfo->pwr[0][1] = (val >> 6) & 0xf;
+ pcinfo->pddac[0][1] = (val >> 10) & 0x3f;
+
+ AR5K_EEPROM_READ(offset++, val);
+ pcinfo->pwr[0][2] = val & 0xf;
+ pcinfo->pddac[0][2] = (val >> 4) & 0x3f;
+
+ pcinfo->pwr[0][3] = 0;
+ pcinfo->pddac[0][3] = 0;
+
+ if (pd_gains > 1) {
+ /*
+ * Pd gain 0 is not the last pd gain
+ * so it only has 2 pd points.
+ * Continue with pd gain 1.
+ */
+ pcinfo->pwr_i[1] = (val >> 10) & 0x1f;
+
+ pcinfo->pddac_i[1] = (val >> 15) & 0x1;
+ AR5K_EEPROM_READ(offset++, val);
+ pcinfo->pddac_i[1] |= (val & 0x3F) << 1;
+
+ pcinfo->pwr[1][0] = (val >> 6) & 0xf;
+ pcinfo->pddac[1][0] = (val >> 10) & 0x3f;
+
+ AR5K_EEPROM_READ(offset++, val);
+ pcinfo->pwr[1][1] = val & 0xf;
+ pcinfo->pddac[1][1] = (val >> 4) & 0x3f;
+ pcinfo->pwr[1][2] = (val >> 10) & 0xf;
+
+ pcinfo->pddac[1][2] = (val >> 14) & 0x3;
+ AR5K_EEPROM_READ(offset++, val);
+ pcinfo->pddac[1][2] |= (val & 0xF) << 2;
+
+ pcinfo->pwr[1][3] = 0;
+ pcinfo->pddac[1][3] = 0;
+ } else if (pd_gains == 1) {
+ /*
+ * Pd gain 0 is the last one so
+ * read the extra point.
+ */
+ pcinfo->pwr[0][3] = (val >> 10) & 0xf;
+
+ pcinfo->pddac[0][3] = (val >> 14) & 0x3;
+ AR5K_EEPROM_READ(offset++, val);
+ pcinfo->pddac[0][3] |= (val & 0xF) << 2;
+ }
+
+ /*
+ * Proceed with the other pd_gains
+ * as above.
+ */
+ if (pd_gains > 2) {
+ pcinfo->pwr_i[2] = (val >> 4) & 0x1f;
+ pcinfo->pddac_i[2] = (val >> 9) & 0x7f;
+
+ AR5K_EEPROM_READ(offset++, val);
+ pcinfo->pwr[2][0] = (val >> 0) & 0xf;
+ pcinfo->pddac[2][0] = (val >> 4) & 0x3f;
+ pcinfo->pwr[2][1] = (val >> 10) & 0xf;
+
+ pcinfo->pddac[2][1] = (val >> 14) & 0x3;
+ AR5K_EEPROM_READ(offset++, val);
+ pcinfo->pddac[2][1] |= (val & 0xF) << 2;
+
+ pcinfo->pwr[2][2] = (val >> 4) & 0xf;
+ pcinfo->pddac[2][2] = (val >> 8) & 0x3f;
+
+ pcinfo->pwr[2][3] = 0;
+ pcinfo->pddac[2][3] = 0;
+ } else if (pd_gains == 2) {
+ pcinfo->pwr[1][3] = (val >> 4) & 0xf;
+ pcinfo->pddac[1][3] = (val >> 8) & 0x3f;
+ }
+
+ if (pd_gains > 3) {
+ pcinfo->pwr_i[3] = (val >> 14) & 0x3;
+ AR5K_EEPROM_READ(offset++, val);
+ pcinfo->pwr_i[3] |= ((val >> 0) & 0x7) << 2;
+
+ pcinfo->pddac_i[3] = (val >> 3) & 0x7f;
+ pcinfo->pwr[3][0] = (val >> 10) & 0xf;
+ pcinfo->pddac[3][0] = (val >> 14) & 0x3;
+
+ AR5K_EEPROM_READ(offset++, val);
+ pcinfo->pddac[3][0] |= (val & 0xF) << 2;
+ pcinfo->pwr[3][1] = (val >> 4) & 0xf;
+ pcinfo->pddac[3][1] = (val >> 8) & 0x3f;
+
+ pcinfo->pwr[3][2] = (val >> 14) & 0x3;
+ AR5K_EEPROM_READ(offset++, val);
+ pcinfo->pwr[3][2] |= ((val >> 0) & 0x3) << 2;
+
+ pcinfo->pddac[3][2] = (val >> 2) & 0x3f;
+ pcinfo->pwr[3][3] = (val >> 8) & 0xf;
+
+ pcinfo->pddac[3][3] = (val >> 12) & 0xF;
+ AR5K_EEPROM_READ(offset++, val);
+ pcinfo->pddac[3][3] |= ((val >> 0) & 0x3) << 4;
+ } else if (pd_gains == 3) {
+ pcinfo->pwr[2][3] = (val >> 14) & 0x3;
+ AR5K_EEPROM_READ(offset++, val);
+ pcinfo->pwr[2][3] |= ((val >> 0) & 0x3) << 2;
+
+ pcinfo->pddac[2][3] = (val >> 2) & 0x3f;
+ }
+ }
+
+ return ath5k_eeprom_convert_pcal_info_2413(ah, mode, chinfo);
+}
+
+
+/*
+ * Read per rate target power (this is the maximum tx power
+ * supported by the card). This info is used when setting
+ * tx power, no matter the channel.
+ *
+ * This also works for v5 EEPROMs.
+ */
+static int
+ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_rate_pcal_info *rate_pcal_info;
+ u8 *rate_target_pwr_num;
+ u32 offset;
+ u16 val;
+ int i;
+
+ offset = AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1);
+ rate_target_pwr_num = &ee->ee_rate_target_pwr_num[mode];
+ switch (mode) {
+ case AR5K_EEPROM_MODE_11A:
+ offset += AR5K_EEPROM_TARGET_PWR_OFF_11A(ee->ee_version);
+ rate_pcal_info = ee->ee_rate_tpwr_a;
+ ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_5GHZ_CHAN;
+ break;
+ case AR5K_EEPROM_MODE_11B:
+ offset += AR5K_EEPROM_TARGET_PWR_OFF_11B(ee->ee_version);
+ rate_pcal_info = ee->ee_rate_tpwr_b;
+ ee->ee_rate_target_pwr_num[mode] = 2; /* 3rd is g mode's 1st */
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ offset += AR5K_EEPROM_TARGET_PWR_OFF_11G(ee->ee_version);
+ rate_pcal_info = ee->ee_rate_tpwr_g;
+ ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_2GHZ_CHAN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Different freq mask for older eeproms (<= v3.2) */
+ if (ee->ee_version <= AR5K_EEPROM_VERSION_3_2) {
+ for (i = 0; i < (*rate_target_pwr_num); i++) {
+ AR5K_EEPROM_READ(offset++, val);
+ rate_pcal_info[i].freq =
+ ath5k_eeprom_bin2freq(ee, (val >> 9) & 0x7f, mode);
+
+ rate_pcal_info[i].target_power_6to24 = ((val >> 3) & 0x3f);
+ rate_pcal_info[i].target_power_36 = (val << 3) & 0x3f;
+
+ AR5K_EEPROM_READ(offset++, val);
+
+ if (rate_pcal_info[i].freq == AR5K_EEPROM_CHANNEL_DIS ||
+ val == 0) {
+ (*rate_target_pwr_num) = i;
+ break;
+ }
+
+ rate_pcal_info[i].target_power_36 |= ((val >> 13) & 0x7);
+ rate_pcal_info[i].target_power_48 = ((val >> 7) & 0x3f);
+ rate_pcal_info[i].target_power_54 = ((val >> 1) & 0x3f);
+ }
+ } else {
+ for (i = 0; i < (*rate_target_pwr_num); i++) {
+ AR5K_EEPROM_READ(offset++, val);
+ rate_pcal_info[i].freq =
+ ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode);
+
+ rate_pcal_info[i].target_power_6to24 = ((val >> 2) & 0x3f);
+ rate_pcal_info[i].target_power_36 = (val << 4) & 0x3f;
+
+ AR5K_EEPROM_READ(offset++, val);
+
+ if (rate_pcal_info[i].freq == AR5K_EEPROM_CHANNEL_DIS ||
+ val == 0) {
+ (*rate_target_pwr_num) = i;
+ break;
+ }
+
+ rate_pcal_info[i].target_power_36 |= (val >> 12) & 0xf;
+ rate_pcal_info[i].target_power_48 = ((val >> 6) & 0x3f);
+ rate_pcal_info[i].target_power_54 = (val & 0x3f);
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Read per channel calibration info from EEPROM
+ *
+ * This info is used to calibrate the baseband power table. Imagine
+ * that for each channel there is a power curve that's hw specific
+ * (depends on amplifier etc) and we try to "correct" this curve using
+ * offsets we pass on to phy chip (baseband -> before amplifier) so that
+ * it can use accurate power values when setting tx power (takes amplifier's
+ * performance on each channel into account).
+ *
+ * EEPROM provides us with the offsets for some pre-calibrated channels
+ * and we have to interpolate to create the full table for these channels and
+ * also the table for any channel.
+ */
+static int
+ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ int (*read_pcal)(struct ath5k_hw *hw, int mode);
+ int mode;
+ int err;
+
+ if ((ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) &&
+ (AR5K_EEPROM_EEMAP(ee->ee_misc0) == 1))
+ read_pcal = ath5k_eeprom_read_pcal_info_5112;
+ else if ((ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_0) &&
+ (AR5K_EEPROM_EEMAP(ee->ee_misc0) == 2))
+ read_pcal = ath5k_eeprom_read_pcal_info_2413;
+ else
+ read_pcal = ath5k_eeprom_read_pcal_info_5111;
+
+
+ for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G;
+ mode++) {
+ err = read_pcal(ah, mode);
+ if (err)
+ return err;
+
+ err = ath5k_eeprom_read_target_rate_pwr_info(ah, mode);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Read conformance test limits used for regulatory control */
+static int
+ath5k_eeprom_read_ctl_info(struct ath5k_hw *ah)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_edge_power *rep;
+ unsigned int fmask, pmask;
+ unsigned int ctl_mode;
+ int i, j;
+ u32 offset;
+ u16 val;
+
+ pmask = AR5K_EEPROM_POWER_M;
+ fmask = AR5K_EEPROM_FREQ_M(ee->ee_version);
+ offset = AR5K_EEPROM_CTL(ee->ee_version);
+ ee->ee_ctls = AR5K_EEPROM_N_CTLS(ee->ee_version);
+ for (i = 0; i < ee->ee_ctls; i += 2) {
+ AR5K_EEPROM_READ(offset++, val);
+ ee->ee_ctl[i] = (val >> 8) & 0xff;
+ ee->ee_ctl[i + 1] = val & 0xff;
+ }
+
+ offset = AR5K_EEPROM_GROUP8_OFFSET;
+ if (ee->ee_version >= AR5K_EEPROM_VERSION_4_0)
+ offset += AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1) -
+ AR5K_EEPROM_GROUP5_OFFSET;
+ else
+ offset += AR5K_EEPROM_GROUPS_START(ee->ee_version);
+
+ rep = ee->ee_ctl_pwr;
+ for (i = 0; i < ee->ee_ctls; i++) {
+ switch (ee->ee_ctl[i] & AR5K_CTL_MODE_M) {
+ case AR5K_CTL_11A:
+ case AR5K_CTL_TURBO:
+ ctl_mode = AR5K_EEPROM_MODE_11A;
+ break;
+ default:
+ ctl_mode = AR5K_EEPROM_MODE_11G;
+ break;
+ }
+ if (ee->ee_ctl[i] == 0) {
+ if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3)
+ offset += 8;
+ else
+ offset += 7;
+ rep += AR5K_EEPROM_N_EDGES;
+ continue;
+ }
+ if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) {
+ for (j = 0; j < AR5K_EEPROM_N_EDGES; j += 2) {
+ AR5K_EEPROM_READ(offset++, val);
+ rep[j].freq = (val >> 8) & fmask;
+ rep[j + 1].freq = val & fmask;
+ }
+ for (j = 0; j < AR5K_EEPROM_N_EDGES; j += 2) {
+ AR5K_EEPROM_READ(offset++, val);
+ rep[j].edge = (val >> 8) & pmask;
+ rep[j].flag = (val >> 14) & 1;
+ rep[j + 1].edge = val & pmask;
+ rep[j + 1].flag = (val >> 6) & 1;
+ }
+ } else {
+ AR5K_EEPROM_READ(offset++, val);
+ rep[0].freq = (val >> 9) & fmask;
+ rep[1].freq = (val >> 2) & fmask;
+ rep[2].freq = (val << 5) & fmask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ rep[2].freq |= (val >> 11) & 0x1f;
+ rep[3].freq = (val >> 4) & fmask;
+ rep[4].freq = (val << 3) & fmask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ rep[4].freq |= (val >> 13) & 0x7;
+ rep[5].freq = (val >> 6) & fmask;
+ rep[6].freq = (val << 1) & fmask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ rep[6].freq |= (val >> 15) & 0x1;
+ rep[7].freq = (val >> 8) & fmask;
+
+ rep[0].edge = (val >> 2) & pmask;
+ rep[1].edge = (val << 4) & pmask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ rep[1].edge |= (val >> 12) & 0xf;
+ rep[2].edge = (val >> 6) & pmask;
+ rep[3].edge = val & pmask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ rep[4].edge = (val >> 10) & pmask;
+ rep[5].edge = (val >> 4) & pmask;
+ rep[6].edge = (val << 2) & pmask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ rep[6].edge |= (val >> 14) & 0x3;
+ rep[7].edge = (val >> 8) & pmask;
+ }
+ for (j = 0; j < AR5K_EEPROM_N_EDGES; j++) {
+ rep[j].freq = ath5k_eeprom_bin2freq(ee,
+ rep[j].freq, ctl_mode);
+ }
+ rep += AR5K_EEPROM_N_EDGES;
+ }
+
+ return 0;
+}
+
+static int
+ath5k_eeprom_read_spur_chans(struct ath5k_hw *ah)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ u32 offset;
+ u16 val;
+ int ret = 0, i;
+
+ offset = AR5K_EEPROM_CTL(ee->ee_version) +
+ AR5K_EEPROM_N_CTLS(ee->ee_version);
+
+ if (ee->ee_version < AR5K_EEPROM_VERSION_5_3) {
+ /* No spur info for 5GHz */
+ ee->ee_spur_chans[0][0] = AR5K_EEPROM_NO_SPUR;
+ /* 2 channels for 2GHz (2464/2420) */
+ ee->ee_spur_chans[0][1] = AR5K_EEPROM_5413_SPUR_CHAN_1;
+ ee->ee_spur_chans[1][1] = AR5K_EEPROM_5413_SPUR_CHAN_2;
+ ee->ee_spur_chans[2][1] = AR5K_EEPROM_NO_SPUR;
+ } else if (ee->ee_version >= AR5K_EEPROM_VERSION_5_3) {
+ for (i = 0; i < AR5K_EEPROM_N_SPUR_CHANS; i++) {
+ AR5K_EEPROM_READ(offset, val);
+ ee->ee_spur_chans[i][0] = val;
+ AR5K_EEPROM_READ(offset + AR5K_EEPROM_N_SPUR_CHANS,
+ val);
+ ee->ee_spur_chans[i][1] = val;
+ offset++;
+ }
+ }
+
+ return ret;
+}
+
+
+/***********************\
+* Init/Detach functions *
+\***********************/
+
+/*
+ * Initialize eeprom data structure
+ */
+int
+ath5k_eeprom_init(struct ath5k_hw *ah)
+{
+ int err;
+
+ err = ath5k_eeprom_init_header(ah);
+ if (err < 0)
+ return err;
+
+ err = ath5k_eeprom_init_modes(ah);
+ if (err < 0)
+ return err;
+
+ err = ath5k_eeprom_read_pcal_info(ah);
+ if (err < 0)
+ return err;
+
+ err = ath5k_eeprom_read_ctl_info(ah);
+ if (err < 0)
+ return err;
+
+ err = ath5k_eeprom_read_spur_chans(ah);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+void
+ath5k_eeprom_detach(struct ath5k_hw *ah)
+{
+ u8 mode;
+
+ for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++)
+ ath5k_eeprom_free_pcal_info(ah, mode);
+}
+
+int
+ath5k_eeprom_mode_from_channel(struct ieee80211_channel *channel)
+{
+ switch (channel->hw_value) {
+ case AR5K_MODE_11A:
+ return AR5K_EEPROM_MODE_11A;
+ case AR5K_MODE_11G:
+ return AR5K_EEPROM_MODE_11G;
+ case AR5K_MODE_11B:
+ return AR5K_EEPROM_MODE_11B;
+ default:
+ return -1;
+ }
+}
diff --git a/drivers/net/wireless/ath/ath5k/eeprom.h b/drivers/net/wireless/ath/ath5k/eeprom.h
new file mode 100644
index 0000000..dc2bcfe
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/eeprom.h
@@ -0,0 +1,497 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ */
+
+/*
+ * Common ar5xxx EEPROM data offsets (set these on AR5K_EEPROM_BASE)
+ */
+#define AR5K_EEPROM_PCIE_OFFSET 0x02 /* Contains offset to PCI-E infos */
+#define AR5K_EEPROM_PCIE_SERDES_SECTION 0x40 /* PCIE_OFFSET points here when
+ * SERDES infos are present */
+#define AR5K_EEPROM_MAGIC 0x003d /* EEPROM Magic number */
+#define AR5K_EEPROM_MAGIC_VALUE 0x5aa5 /* Default - found on EEPROM */
+
+#define AR5K_EEPROM_IS_HB63 0x000b /* Talon detect */
+
+#define AR5K_EEPROM_RFKILL 0x0f
+#define AR5K_EEPROM_RFKILL_GPIO_SEL 0x0000001c
+#define AR5K_EEPROM_RFKILL_GPIO_SEL_S 2
+#define AR5K_EEPROM_RFKILL_POLARITY 0x00000002
+#define AR5K_EEPROM_RFKILL_POLARITY_S 1
+
+#define AR5K_EEPROM_REG_DOMAIN 0x00bf /* EEPROM regdom */
+
+/* FLASH(EEPROM) Defines for AR531X chips */
+#define AR5K_EEPROM_SIZE_LOWER 0x1b /* size info -- lower */
+#define AR5K_EEPROM_SIZE_UPPER 0x1c /* size info -- upper */
+#define AR5K_EEPROM_SIZE_UPPER_MASK 0xfff0
+#define AR5K_EEPROM_SIZE_UPPER_SHIFT 4
+#define AR5K_EEPROM_SIZE_ENDLOC_SHIFT 12
+
+#define AR5K_EEPROM_CHECKSUM 0x00c0 /* EEPROM checksum */
+#define AR5K_EEPROM_INFO_BASE 0x00c0 /* EEPROM header */
+#define AR5K_EEPROM_INFO_MAX (0x400 - AR5K_EEPROM_INFO_BASE)
+#define AR5K_EEPROM_INFO_CKSUM 0xffff
+#define AR5K_EEPROM_INFO(_n) (AR5K_EEPROM_INFO_BASE + (_n))
+
+#define AR5K_EEPROM_VERSION AR5K_EEPROM_INFO(1) /* EEPROM Version */
+#define AR5K_EEPROM_VERSION_3_0 0x3000 /* No idea what's going on before this version */
+#define AR5K_EEPROM_VERSION_3_1 0x3001 /* ob/db values for 2GHz (ar5211_rfregs) */
+#define AR5K_EEPROM_VERSION_3_2 0x3002 /* different frequency representation (eeprom_bin2freq) */
+#define AR5K_EEPROM_VERSION_3_3 0x3003 /* offsets changed, has 32 CTLs (see below) and ee_false_detect (eeprom_read_modes) */
+#define AR5K_EEPROM_VERSION_3_4 0x3004 /* has ee_i_gain, ee_cck_ofdm_power_delta (eeprom_read_modes) */
+#define AR5K_EEPROM_VERSION_4_0 0x4000 /* has ee_misc, ee_cal_pier, ee_turbo_max_power and ee_xr_power (eeprom_init) */
+#define AR5K_EEPROM_VERSION_4_1 0x4001 /* has ee_margin_tx_rx (eeprom_init) */
+#define AR5K_EEPROM_VERSION_4_2 0x4002 /* has ee_cck_ofdm_gain_delta (eeprom_init) */
+#define AR5K_EEPROM_VERSION_4_3 0x4003 /* power calibration changes */
+#define AR5K_EEPROM_VERSION_4_4 0x4004
+#define AR5K_EEPROM_VERSION_4_5 0x4005
+#define AR5K_EEPROM_VERSION_4_6 0x4006 /* has ee_scaled_cck_delta */
+#define AR5K_EEPROM_VERSION_4_7 0x3007 /* 4007 ? */
+#define AR5K_EEPROM_VERSION_4_9 0x4009 /* EAR futureproofing */
+#define AR5K_EEPROM_VERSION_5_0 0x5000 /* Has 2413 PDADC calibration etc */
+#define AR5K_EEPROM_VERSION_5_1 0x5001 /* Has capability values */
+#define AR5K_EEPROM_VERSION_5_3 0x5003 /* Has spur mitigation tables */
+
+#define AR5K_EEPROM_MODE_11A 0
+#define AR5K_EEPROM_MODE_11B 1
+#define AR5K_EEPROM_MODE_11G 2
+
+#define AR5K_EEPROM_HDR AR5K_EEPROM_INFO(2) /* Header that contains the device caps */
+#define AR5K_EEPROM_HDR_11A(_v) (((_v) >> AR5K_EEPROM_MODE_11A) & 0x1)
+#define AR5K_EEPROM_HDR_11B(_v) (((_v) >> AR5K_EEPROM_MODE_11B) & 0x1)
+#define AR5K_EEPROM_HDR_11G(_v) (((_v) >> AR5K_EEPROM_MODE_11G) & 0x1)
+#define AR5K_EEPROM_HDR_T_2GHZ_DIS(_v) (((_v) >> 3) & 0x1) /* Disable turbo for 2GHz */
+#define AR5K_EEPROM_HDR_T_5GHZ_DBM(_v) (((_v) >> 4) & 0x7f) /* Max turbo power for < 2W power consumption */
+#define AR5K_EEPROM_HDR_DEVICE(_v) (((_v) >> 11) & 0x7) /* Device type (1 Cardbus, 2 PCI, 3 MiniPCI, 4 AP) */
+#define AR5K_EEPROM_HDR_RFKILL(_v) (((_v) >> 14) & 0x1) /* Device has RFKill support */
+#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v) (((_v) >> 15) & 0x1) /* Disable turbo for 5GHz */
+
+/* Newer EEPROMs are using a different offset */
+#define AR5K_EEPROM_OFF(_v, _v3_0, _v3_3) \
+ (((_v) >= AR5K_EEPROM_VERSION_3_3) ? _v3_3 : _v3_0)
+
+#define AR5K_EEPROM_ANT_GAIN(_v) AR5K_EEPROM_OFF(_v, 0x00c4, 0x00c3)
+#define AR5K_EEPROM_ANT_GAIN_5GHZ(_v) ((s8)(((_v) >> 8) & 0xff))
+#define AR5K_EEPROM_ANT_GAIN_2GHZ(_v) ((s8)((_v) & 0xff))
+
+/* Misc values available since EEPROM 4.0 */
+#define AR5K_EEPROM_MISC0 AR5K_EEPROM_INFO(4)
+#define AR5K_EEPROM_EARSTART(_v) ((_v) & 0xfff)
+#define AR5K_EEPROM_HDR_XR2_DIS(_v) (((_v) >> 12) & 0x1)
+#define AR5K_EEPROM_HDR_XR5_DIS(_v) (((_v) >> 13) & 0x1)
+#define AR5K_EEPROM_EEMAP(_v) (((_v) >> 14) & 0x3)
+
+#define AR5K_EEPROM_MISC1 AR5K_EEPROM_INFO(5)
+#define AR5K_EEPROM_TARGET_PWRSTART(_v) ((_v) & 0xfff)
+#define AR5K_EEPROM_HAS32KHZCRYSTAL(_v) (((_v) >> 14) & 0x1) /* has 32KHz crystal for sleep mode */
+#define AR5K_EEPROM_HAS32KHZCRYSTAL_OLD(_v) (((_v) >> 15) & 0x1)
+
+#define AR5K_EEPROM_MISC2 AR5K_EEPROM_INFO(6)
+#define AR5K_EEPROM_EEP_FILE_VERSION(_v) (((_v) >> 8) & 0xff)
+#define AR5K_EEPROM_EAR_FILE_VERSION(_v) ((_v) & 0xff)
+
+#define AR5K_EEPROM_MISC3 AR5K_EEPROM_INFO(7)
+#define AR5K_EEPROM_ART_BUILD_NUM(_v) (((_v) >> 10) & 0x3f)
+#define AR5K_EEPROM_EAR_FILE_ID(_v) ((_v) & 0xff)
+
+#define AR5K_EEPROM_MISC4 AR5K_EEPROM_INFO(8)
+#define AR5K_EEPROM_CAL_DATA_START(_v) (((_v) >> 4) & 0xfff)
+#define AR5K_EEPROM_MASK_R0(_v) (((_v) >> 2) & 0x3) /* modes supported by radio 0 (bit 1: G, bit 2: A) */
+#define AR5K_EEPROM_MASK_R1(_v) ((_v) & 0x3) /* modes supported by radio 1 (bit 1: G, bit 2: A) */
+
+#define AR5K_EEPROM_MISC5 AR5K_EEPROM_INFO(9)
+#define AR5K_EEPROM_COMP_DIS(_v) ((_v) & 0x1) /* disable compression */
+#define AR5K_EEPROM_AES_DIS(_v) (((_v) >> 1) & 0x1) /* disable AES */
+#define AR5K_EEPROM_FF_DIS(_v) (((_v) >> 2) & 0x1) /* disable fast frames */
+#define AR5K_EEPROM_BURST_DIS(_v) (((_v) >> 3) & 0x1) /* disable bursting */
+#define AR5K_EEPROM_MAX_QCU(_v) (((_v) >> 4) & 0xf) /* max number of QCUs. defaults to 10 */
+#define AR5K_EEPROM_HEAVY_CLIP_EN(_v) (((_v) >> 8) & 0x1) /* enable heavy clipping */
+#define AR5K_EEPROM_KEY_CACHE_SIZE(_v) (((_v) >> 12) & 0xf) /* key cache size. defaults to 128 */
+
+#define AR5K_EEPROM_MISC6 AR5K_EEPROM_INFO(10)
+#define AR5K_EEPROM_TX_CHAIN_DIS ((_v) & 0x7) /* MIMO chains disabled for TX bitmask */
+#define AR5K_EEPROM_RX_CHAIN_DIS (((_v) >> 3) & 0x7) /* MIMO chains disabled for RX bitmask */
+#define AR5K_EEPROM_FCC_MID_EN (((_v) >> 6) & 0x1) /* 5.47-5.7GHz supported */
+#define AR5K_EEPROM_JAP_U1EVEN_EN (((_v) >> 7) & 0x1) /* Japan UNII1 band (5.15-5.25GHz) on even channels (5180, 5200, 5220, 5240) supported */
+#define AR5K_EEPROM_JAP_U2_EN (((_v) >> 8) & 0x1) /* Japan UNII2 band (5.25-5.35GHz) supported */
+#define AR5K_EEPROM_JAP_MID_EN (((_v) >> 9) & 0x1) /* Japan band from 5.47-5.7GHz supported */
+#define AR5K_EEPROM_JAP_U1ODD_EN (((_v) >> 10) & 0x1) /* Japan UNII2 band (5.15-5.25GHz) on odd channels (5170, 5190, 5210, 5230) supported */
+#define AR5K_EEPROM_JAP_11A_NEW_EN (((_v) >> 11) & 0x1) /* Japan A mode enabled (using even channels) */
+
+/* calibration settings */
+#define AR5K_EEPROM_MODES_11A(_v) AR5K_EEPROM_OFF(_v, 0x00c5, 0x00d4)
+#define AR5K_EEPROM_MODES_11B(_v) AR5K_EEPROM_OFF(_v, 0x00d0, 0x00f2)
+#define AR5K_EEPROM_MODES_11G(_v) AR5K_EEPROM_OFF(_v, 0x00da, 0x010d)
+#define AR5K_EEPROM_CTL(_v) AR5K_EEPROM_OFF(_v, 0x00e4, 0x0128) /* Conformance test limits */
+#define AR5K_EEPROM_GROUPS_START(_v) AR5K_EEPROM_OFF(_v, 0x0100, 0x0150) /* Start of Groups */
+#define AR5K_EEPROM_GROUP1_OFFSET 0x0
+#define AR5K_EEPROM_GROUP2_OFFSET 0x5
+#define AR5K_EEPROM_GROUP3_OFFSET 0x37
+#define AR5K_EEPROM_GROUP4_OFFSET 0x46
+#define AR5K_EEPROM_GROUP5_OFFSET 0x55
+#define AR5K_EEPROM_GROUP6_OFFSET 0x65
+#define AR5K_EEPROM_GROUP7_OFFSET 0x69
+#define AR5K_EEPROM_GROUP8_OFFSET 0x6f
+
+#define AR5K_EEPROM_TARGET_PWR_OFF_11A(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \
+ AR5K_EEPROM_GROUP5_OFFSET, 0x0000)
+#define AR5K_EEPROM_TARGET_PWR_OFF_11B(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \
+ AR5K_EEPROM_GROUP6_OFFSET, 0x0010)
+#define AR5K_EEPROM_TARGET_PWR_OFF_11G(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \
+ AR5K_EEPROM_GROUP7_OFFSET, 0x0014)
+
+/* [3.1 - 3.3] */
+#define AR5K_EEPROM_OBDB0_2GHZ 0x00ec
+#define AR5K_EEPROM_OBDB1_2GHZ 0x00ed
+
+#define AR5K_EEPROM_PROTECT 0x003f /* EEPROM protect status */
+#define AR5K_EEPROM_PROTECT_RD_0_31 0x0001 /* Read protection bit for offsets 0x0 - 0x1f */
+#define AR5K_EEPROM_PROTECT_WR_0_31 0x0002 /* Write protection bit for offsets 0x0 - 0x1f */
+#define AR5K_EEPROM_PROTECT_RD_32_63 0x0004 /* 0x20 - 0x3f */
+#define AR5K_EEPROM_PROTECT_WR_32_63 0x0008
+#define AR5K_EEPROM_PROTECT_RD_64_127 0x0010 /* 0x40 - 0x7f */
+#define AR5K_EEPROM_PROTECT_WR_64_127 0x0020
+#define AR5K_EEPROM_PROTECT_RD_128_191 0x0040 /* 0x80 - 0xbf (regdom) */
+#define AR5K_EEPROM_PROTECT_WR_128_191 0x0080
+#define AR5K_EEPROM_PROTECT_RD_192_207 0x0100 /* 0xc0 - 0xcf */
+#define AR5K_EEPROM_PROTECT_WR_192_207 0x0200
+#define AR5K_EEPROM_PROTECT_RD_208_223 0x0400 /* 0xd0 - 0xdf */
+#define AR5K_EEPROM_PROTECT_WR_208_223 0x0800
+#define AR5K_EEPROM_PROTECT_RD_224_239 0x1000 /* 0xe0 - 0xef */
+#define AR5K_EEPROM_PROTECT_WR_224_239 0x2000
+#define AR5K_EEPROM_PROTECT_RD_240_255 0x4000 /* 0xf0 - 0xff */
+#define AR5K_EEPROM_PROTECT_WR_240_255 0x8000
+
+/* Some EEPROM defines */
+#define AR5K_EEPROM_EEP_SCALE 100
+#define AR5K_EEPROM_EEP_DELTA 10
+#define AR5K_EEPROM_N_MODES 3
+#define AR5K_EEPROM_N_5GHZ_CHAN 10
+#define AR5K_EEPROM_N_2GHZ_CHAN 3
+#define AR5K_EEPROM_N_2GHZ_CHAN_2413 4
+#define AR5K_EEPROM_N_2GHZ_CHAN_MAX 4
+#define AR5K_EEPROM_MAX_CHAN 10
+#define AR5K_EEPROM_N_PWR_POINTS_5111 11
+#define AR5K_EEPROM_N_PCDAC 11
+#define AR5K_EEPROM_N_PHASE_CAL 5
+#define AR5K_EEPROM_N_TEST_FREQ 8
+#define AR5K_EEPROM_N_EDGES 8
+#define AR5K_EEPROM_N_INTERCEPTS 11
+#define AR5K_EEPROM_FREQ_M(_v) AR5K_EEPROM_OFF(_v, 0x7f, 0xff)
+#define AR5K_EEPROM_PCDAC_M 0x3f
+#define AR5K_EEPROM_PCDAC_START 1
+#define AR5K_EEPROM_PCDAC_STOP 63
+#define AR5K_EEPROM_PCDAC_STEP 1
+#define AR5K_EEPROM_NON_EDGE_M 0x40
+#define AR5K_EEPROM_CHANNEL_POWER 8
+#define AR5K_EEPROM_N_OBDB 4
+#define AR5K_EEPROM_OBDB_DIS 0xffff
+#define AR5K_EEPROM_CHANNEL_DIS 0xff
+#define AR5K_EEPROM_SCALE_OC_DELTA(_x) (((_x) * 2) / 10)
+#define AR5K_EEPROM_N_CTLS(_v) AR5K_EEPROM_OFF(_v, 16, 32)
+#define AR5K_EEPROM_MAX_CTLS 32
+#define AR5K_EEPROM_N_PD_CURVES 4
+#define AR5K_EEPROM_N_XPD0_POINTS 4
+#define AR5K_EEPROM_N_XPD3_POINTS 3
+#define AR5K_EEPROM_N_PD_GAINS 4
+#define AR5K_EEPROM_N_PD_POINTS 5
+#define AR5K_EEPROM_N_INTERCEPT_10_2GHZ 35
+#define AR5K_EEPROM_N_INTERCEPT_10_5GHZ 55
+#define AR5K_EEPROM_POWER_M 0x3f
+#define AR5K_EEPROM_POWER_MIN 0
+#define AR5K_EEPROM_POWER_MAX 3150
+#define AR5K_EEPROM_POWER_STEP 50
+#define AR5K_EEPROM_POWER_TABLE_SIZE 64
+#define AR5K_EEPROM_N_POWER_LOC_11B 4
+#define AR5K_EEPROM_N_POWER_LOC_11G 6
+#define AR5K_EEPROM_I_GAIN 10
+#define AR5K_EEPROM_CCK_OFDM_DELTA 15
+#define AR5K_EEPROM_N_IQ_CAL 2
+/* 5GHz/2GHz */
+enum ath5k_eeprom_freq_bands {
+ AR5K_EEPROM_BAND_5GHZ = 0,
+ AR5K_EEPROM_BAND_2GHZ = 1,
+ AR5K_EEPROM_N_FREQ_BANDS,
+};
+/* Spur chans per freq band */
+#define AR5K_EEPROM_N_SPUR_CHANS 5
+/* fbin value for chan 2464 x2 */
+#define AR5K_EEPROM_5413_SPUR_CHAN_1 1640
+/* fbin value for chan 2420 x2 */
+#define AR5K_EEPROM_5413_SPUR_CHAN_2 1200
+#define AR5K_EEPROM_SPUR_CHAN_MASK 0x3FFF
+#define AR5K_EEPROM_NO_SPUR 0x8000
+#define AR5K_SPUR_CHAN_WIDTH 87
+#define AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz 3125
+#define AR5K_SPUR_SYMBOL_WIDTH_TURBO_100Hz 6250
+
+#define AR5K_EEPROM_READ(_o, _v) do { \
+ if (!ath5k_hw_nvram_read(ah, (_o), &(_v))) \
+ return -EIO; \
+} while (0)
+
+#define AR5K_EEPROM_READ_HDR(_o, _v) \
+ AR5K_EEPROM_READ(_o, ah->ah_capabilities.cap_eeprom._v); \
+
+enum ath5k_ant_table {
+ AR5K_ANT_CTL = 0, /* Idle switch table settings */
+ AR5K_ANT_SWTABLE_A = 1, /* Switch table for antenna A */
+ AR5K_ANT_SWTABLE_B = 2, /* Switch table for antenna B */
+ AR5K_ANT_MAX,
+};
+
+enum ath5k_ctl_mode {
+ AR5K_CTL_11A = 0,
+ AR5K_CTL_11B = 1,
+ AR5K_CTL_11G = 2,
+ AR5K_CTL_TURBO = 3,
+ AR5K_CTL_TURBOG = 4,
+ AR5K_CTL_2GHT20 = 5,
+ AR5K_CTL_5GHT20 = 6,
+ AR5K_CTL_2GHT40 = 7,
+ AR5K_CTL_5GHT40 = 8,
+ AR5K_CTL_MODE_M = 15,
+};
+
+/* Per channel calibration data, used for power table setup */
+struct ath5k_chan_pcal_info_rf5111 {
+ /* Power levels in half dBm units
+ * for one power curve. */
+ u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111];
+ /* PCDAC table steps
+ * for the above values */
+ u8 pcdac[AR5K_EEPROM_N_PWR_POINTS_5111];
+ /* Starting PCDAC step */
+ u8 pcdac_min;
+ /* Final PCDAC step */
+ u8 pcdac_max;
+};
+
+struct ath5k_chan_pcal_info_rf5112 {
+ /* Power levels in quarter dBm units
+ * for lower (0) and higher (3)
+ * level curves in 0.25dB units */
+ s8 pwr_x0[AR5K_EEPROM_N_XPD0_POINTS];
+ s8 pwr_x3[AR5K_EEPROM_N_XPD3_POINTS];
+ /* PCDAC table steps
+ * for the above values */
+ u8 pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS];
+ u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS];
+};
+
+struct ath5k_chan_pcal_info_rf2413 {
+ /* Starting pwr/pddac values */
+ s8 pwr_i[AR5K_EEPROM_N_PD_GAINS];
+ u8 pddac_i[AR5K_EEPROM_N_PD_GAINS];
+ /* (pwr,pddac) points
+ * power levels in 0.5dB units */
+ s8 pwr[AR5K_EEPROM_N_PD_GAINS]
+ [AR5K_EEPROM_N_PD_POINTS];
+ u8 pddac[AR5K_EEPROM_N_PD_GAINS]
+ [AR5K_EEPROM_N_PD_POINTS];
+};
+
+enum ath5k_powertable_type {
+ AR5K_PWRTABLE_PWR_TO_PCDAC = 0,
+ AR5K_PWRTABLE_LINEAR_PCDAC = 1,
+ AR5K_PWRTABLE_PWR_TO_PDADC = 2,
+};
+
+struct ath5k_pdgain_info {
+ u8 pd_points;
+ u8 *pd_step;
+ /* Power values are in
+ * 0.25dB units */
+ s16 *pd_pwr;
+};
+
+struct ath5k_chan_pcal_info {
+ /* Frequency */
+ u16 freq;
+ /* Tx power boundaries */
+ s16 max_pwr;
+ s16 min_pwr;
+ union {
+ struct ath5k_chan_pcal_info_rf5111 rf5111_info;
+ struct ath5k_chan_pcal_info_rf5112 rf5112_info;
+ struct ath5k_chan_pcal_info_rf2413 rf2413_info;
+ };
+ /* Raw values used by phy code
+ * Curves are stored in order from lower
+ * gain to higher gain (max txpower -> min txpower) */
+ struct ath5k_pdgain_info *pd_curves;
+};
+
+/* Per rate calibration data for each mode,
+ * used for rate power table setup.
+ * Note: Values in 0.5dB units */
+struct ath5k_rate_pcal_info {
+ u16 freq; /* Frequency */
+ /* Power level for 6-24Mbit/s rates or
+ * 1Mb rate */
+ u16 target_power_6to24;
+ /* Power level for 36Mbit rate or
+ * 2Mb rate */
+ u16 target_power_36;
+ /* Power level for 48Mbit rate or
+ * 5.5Mbit rate */
+ u16 target_power_48;
+ /* Power level for 54Mbit rate or
+ * 11Mbit rate */
+ u16 target_power_54;
+};
+
+/* Power edges for conformance test limits */
+struct ath5k_edge_power {
+ u16 freq;
+ u16 edge; /* in half dBm */
+ bool flag;
+};
+
+/**
+ * struct ath5k_eeprom_info - EEPROM calibration data
+ *
+ * @ee_regdomain: ath/regd.c takes care of COUNTRY_ERD and WORLDWIDE_ROAMING
+ * flags
+ * @ee_ant_gain: Antenna gain in 0.5dB steps signed [5211 only?]
+ * @ee_cck_ofdm_gain_delta: difference in gainF to output the same power for
+ * OFDM and CCK packets
+ * @ee_cck_ofdm_power_delta: power difference between OFDM (6Mbps) and CCK
+ * (11Mbps) rate in G mode. 0.1dB steps
+ * @ee_scaled_cck_delta: for Japan Channel 14: 0.1dB resolution
+ *
+ * @ee_i_cal: Initial I coefficient to correct I/Q mismatch in the receive path
+ * @ee_q_cal: Initial Q coefficient to correct I/Q mismatch in the receive path
+ * @ee_fixed_bias: use ee_ob and ee_db settings or use automatic control
+ * @ee_switch_settling: RX/TX Switch settling time
+ * @ee_atn_tx_rx: Difference in attenuation between TX and RX in 1dB steps
+ * @ee_ant_control: Antenna Control Settings
+ * @ee_ob: Bias current for Output stage of PA
+ * B/G mode: Index [0] is used for AR2112/5112, otherwise [1]
+ * A mode: [0] 5.15-5.25 [1] 5.25-5.50 [2] 5.50-5.70 [3] 5.70-5.85 GHz
+ * @ee_db: Bias current for Output stage of PA. see @ee_ob
+ * @ee_tx_end2xlna_enable: Time difference from when BB finishes sending a frame
+ * to when the external LNA is activated
+ * @ee_tx_end2xpa_disable: Time difference from when BB finishes sending a frame
+ * to when the external PA switch is deactivated
+ * @ee_tx_frm2xpa_enable: Time difference from when MAC sends frame to when
+ * external PA switch is activated
+ * @ee_thr_62: Clear Channel Assessment (CCA) sensitivity
+ * (IEEE802.11a section 17.3.10.5 )
+ * @ee_xlna_gain: Total gain of the LNA (information only)
+ * @ee_xpd: Use external (1) or internal power detector
+ * @ee_x_gain: Gain for external power detector output (differences in EEMAP
+ * versions!)
+ * @ee_i_gain: Initial gain value after reset
+ * @ee_margin_tx_rx: Margin in dB when final attenuation stage should be used
+ *
+ * @ee_false_detect: Backoff in Sensitivity (dB) on channels with spur signals
+ * @ee_noise_floor_thr: Noise floor threshold in 1dB steps
+ * @ee_adc_desired_size: Desired amplitude for ADC, used by AGC; in 0.5 dB steps
+ * @ee_pga_desired_size: Desired output of PGA (for BB gain) in 0.5 dB steps
+ * @ee_pd_gain_overlap: PD ADC curves need to overlap in 0.5dB steps (ee_map>=2)
+ */
+struct ath5k_eeprom_info {
+
+ /* Header information */
+ u16 ee_magic;
+ u16 ee_protect;
+ u16 ee_regdomain;
+ u16 ee_version;
+ u16 ee_header;
+ u16 ee_ant_gain;
+ u8 ee_rfkill_pin;
+ bool ee_rfkill_pol;
+ bool ee_is_hb63;
+ bool ee_serdes;
+ u16 ee_misc0;
+ u16 ee_misc1;
+ u16 ee_misc2;
+ u16 ee_misc3;
+ u16 ee_misc4;
+ u16 ee_misc5;
+ u16 ee_misc6;
+ u16 ee_cck_ofdm_gain_delta;
+ u16 ee_cck_ofdm_power_delta;
+ u16 ee_scaled_cck_delta;
+
+ /* RF Calibration settings (reset, rfregs) */
+ u16 ee_i_cal[AR5K_EEPROM_N_MODES];
+ u16 ee_q_cal[AR5K_EEPROM_N_MODES];
+ u16 ee_fixed_bias[AR5K_EEPROM_N_MODES];
+ u16 ee_turbo_max_power[AR5K_EEPROM_N_MODES];
+ u16 ee_xr_power[AR5K_EEPROM_N_MODES];
+ u16 ee_switch_settling[AR5K_EEPROM_N_MODES];
+ u16 ee_atn_tx_rx[AR5K_EEPROM_N_MODES];
+ u16 ee_ant_control[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PCDAC];
+ u16 ee_ob[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB];
+ u16 ee_db[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB];
+ u16 ee_tx_end2xlna_enable[AR5K_EEPROM_N_MODES];
+ u16 ee_tx_end2xpa_disable[AR5K_EEPROM_N_MODES];
+ u16 ee_tx_frm2xpa_enable[AR5K_EEPROM_N_MODES];
+ u16 ee_thr_62[AR5K_EEPROM_N_MODES];
+ u16 ee_xlna_gain[AR5K_EEPROM_N_MODES];
+ u16 ee_xpd[AR5K_EEPROM_N_MODES];
+ u16 ee_x_gain[AR5K_EEPROM_N_MODES];
+ u16 ee_i_gain[AR5K_EEPROM_N_MODES];
+ u16 ee_margin_tx_rx[AR5K_EEPROM_N_MODES];
+ u16 ee_switch_settling_turbo[AR5K_EEPROM_N_MODES];
+ u16 ee_margin_tx_rx_turbo[AR5K_EEPROM_N_MODES];
+ u16 ee_atn_tx_rx_turbo[AR5K_EEPROM_N_MODES];
+
+ /* Power calibration data */
+ u16 ee_false_detect[AR5K_EEPROM_N_MODES];
+
+ /* Number of pd gain curves per mode */
+ u8 ee_pd_gains[AR5K_EEPROM_N_MODES];
+ /* Back mapping pdcurve number -> pdcurve index in pd->pd_curves */
+ u8 ee_pdc_to_idx[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PD_GAINS];
+
+ u8 ee_n_piers[AR5K_EEPROM_N_MODES];
+ struct ath5k_chan_pcal_info ee_pwr_cal_a[AR5K_EEPROM_N_5GHZ_CHAN];
+ struct ath5k_chan_pcal_info ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
+ struct ath5k_chan_pcal_info ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
+
+ /* Per rate target power levels */
+ u8 ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES];
+ struct ath5k_rate_pcal_info ee_rate_tpwr_a[AR5K_EEPROM_N_5GHZ_CHAN];
+ struct ath5k_rate_pcal_info ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
+ struct ath5k_rate_pcal_info ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
+
+ /* Conformance test limits (Unused) */
+ u8 ee_ctls;
+ u8 ee_ctl[AR5K_EEPROM_MAX_CTLS];
+ struct ath5k_edge_power ee_ctl_pwr[AR5K_EEPROM_N_EDGES * AR5K_EEPROM_MAX_CTLS];
+
+ /* Noise Floor Calibration settings */
+ s16 ee_noise_floor_thr[AR5K_EEPROM_N_MODES];
+ s8 ee_adc_desired_size[AR5K_EEPROM_N_MODES];
+ s8 ee_pga_desired_size[AR5K_EEPROM_N_MODES];
+ s8 ee_adc_desired_size_turbo[AR5K_EEPROM_N_MODES];
+ s8 ee_pga_desired_size_turbo[AR5K_EEPROM_N_MODES];
+ s8 ee_pd_gain_overlap;
+
+ /* Spur mitigation data (fbin values for spur channels) */
+ u16 ee_spur_chans[AR5K_EEPROM_N_SPUR_CHANS][AR5K_EEPROM_N_FREQ_BANDS];
+
+ /* Antenna raw switch tables */
+ u32 ee_antenna[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX];
+};
+
+int
+ath5k_eeprom_mode_from_channel(struct ieee80211_channel *channel);
diff --git a/drivers/net/wireless/ath/ath5k/gpio.c b/drivers/net/wireless/ath/ath5k/gpio.c
new file mode 100644
index 0000000..8592978
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/gpio.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ */
+
+/****************\
+ GPIO Functions
+\****************/
+
+#include "ath5k.h"
+#include "reg.h"
+#include "debug.h"
+
+/*
+ * Set led state
+ */
+void ath5k_hw_set_ledstate(struct ath5k_hw *ah, unsigned int state)
+{
+ u32 led;
+ /*5210 has different led mode handling*/
+ u32 led_5210;
+
+ /*Reset led status*/
+ if (ah->ah_version != AR5K_AR5210)
+ AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG,
+ AR5K_PCICFG_LEDMODE | AR5K_PCICFG_LED);
+ else
+ AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_LED);
+
+ /*
+ * Some blinking values, define at your wish
+ */
+ switch (state) {
+ case AR5K_LED_SCAN:
+ case AR5K_LED_AUTH:
+ led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_PEND;
+ led_5210 = AR5K_PCICFG_LED_PEND | AR5K_PCICFG_LED_BCTL;
+ break;
+
+ case AR5K_LED_INIT:
+ led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_NONE;
+ led_5210 = AR5K_PCICFG_LED_PEND;
+ break;
+
+ case AR5K_LED_ASSOC:
+ case AR5K_LED_RUN:
+ led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_ASSOC;
+ led_5210 = AR5K_PCICFG_LED_ASSOC;
+ break;
+
+ default:
+ led = AR5K_PCICFG_LEDMODE_PROM | AR5K_PCICFG_LED_NONE;
+ led_5210 = AR5K_PCICFG_LED_PEND;
+ break;
+ }
+
+ /*Write new status to the register*/
+ if (ah->ah_version != AR5K_AR5210)
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led);
+ else
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led_5210);
+}
+
+/*
+ * Set GPIO inputs
+ */
+int ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio)
+{
+ if (gpio >= AR5K_NUM_GPIO)
+ return -EINVAL;
+
+ ath5k_hw_reg_write(ah,
+ (ath5k_hw_reg_read(ah, AR5K_GPIOCR) & ~AR5K_GPIOCR_OUT(gpio))
+ | AR5K_GPIOCR_IN(gpio), AR5K_GPIOCR);
+
+ return 0;
+}
+
+/*
+ * Set GPIO outputs
+ */
+int ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio)
+{
+ if (gpio >= AR5K_NUM_GPIO)
+ return -EINVAL;
+
+ ath5k_hw_reg_write(ah,
+ (ath5k_hw_reg_read(ah, AR5K_GPIOCR) & ~AR5K_GPIOCR_OUT(gpio))
+ | AR5K_GPIOCR_OUT(gpio), AR5K_GPIOCR);
+
+ return 0;
+}
+
+/*
+ * Get GPIO state
+ */
+u32 ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio)
+{
+ if (gpio >= AR5K_NUM_GPIO)
+ return 0xffffffff;
+
+ /* GPIO input magic */
+ return ((ath5k_hw_reg_read(ah, AR5K_GPIODI) & AR5K_GPIODI_M) >> gpio) &
+ 0x1;
+}
+
+/*
+ * Set GPIO state
+ */
+int ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val)
+{
+ u32 data;
+
+ if (gpio >= AR5K_NUM_GPIO)
+ return -EINVAL;
+
+ /* GPIO output magic */
+ data = ath5k_hw_reg_read(ah, AR5K_GPIODO);
+
+ data &= ~(1 << gpio);
+ data |= (val & 1) << gpio;
+
+ ath5k_hw_reg_write(ah, data, AR5K_GPIODO);
+
+ return 0;
+}
+
+/*
+ * Initialize the GPIO interrupt (RFKill switch)
+ */
+void ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio,
+ u32 interrupt_level)
+{
+ u32 data;
+
+ if (gpio >= AR5K_NUM_GPIO)
+ return;
+
+ /*
+ * Set the GPIO interrupt
+ */
+ data = (ath5k_hw_reg_read(ah, AR5K_GPIOCR) &
+ ~(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_SELH |
+ AR5K_GPIOCR_INT_ENA | AR5K_GPIOCR_OUT(gpio))) |
+ (AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_ENA);
+
+ ath5k_hw_reg_write(ah, interrupt_level ? data :
+ (data | AR5K_GPIOCR_INT_SELH), AR5K_GPIOCR);
+
+ ah->ah_imr |= AR5K_IMR_GPIO;
+
+ /* Enable GPIO interrupts */
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PIMR, AR5K_IMR_GPIO);
+}
+
diff --git a/drivers/net/wireless/ath/ath5k/initvals.c b/drivers/net/wireless/ath/ath5k/initvals.c
new file mode 100644
index 0000000..1ffecc0
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/initvals.c
@@ -0,0 +1,1571 @@
+/*
+ * Initial register settings functions
+ *
+ * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com>
+ * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com>
+ *
+ * Permission to use, copy, modify, and 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 "ath5k.h"
+#include "reg.h"
+#include "debug.h"
+
+/*
+ * Mode-independent initial register writes
+ */
+
+struct ath5k_ini {
+ u16 ini_register;
+ u32 ini_value;
+
+ enum {
+ AR5K_INI_WRITE = 0, /* Default */
+ AR5K_INI_READ = 1, /* Cleared on read */
+ } ini_mode;
+};
+
+/*
+ * Mode specific initial register values
+ */
+
+struct ath5k_ini_mode {
+ u16 mode_register;
+ u32 mode_value[3];
+};
+
+/* Initial register settings for AR5210 */
+static const struct ath5k_ini ar5210_ini[] = {
+ /* PCU and MAC registers */
+ { AR5K_NOQCU_TXDP0, 0 },
+ { AR5K_NOQCU_TXDP1, 0 },
+ { AR5K_RXDP, 0 },
+ { AR5K_CR, 0 },
+ { AR5K_ISR, 0, AR5K_INI_READ },
+ { AR5K_IMR, 0 },
+ { AR5K_IER, AR5K_IER_DISABLE },
+ { AR5K_BSR, 0, AR5K_INI_READ },
+ { AR5K_TXCFG, AR5K_DMASIZE_128B },
+ { AR5K_RXCFG, AR5K_DMASIZE_128B },
+ { AR5K_CFG, AR5K_INIT_CFG },
+ { AR5K_TOPS, 8 },
+ { AR5K_RXNOFRM, 8 },
+ { AR5K_RPGTO, 0 },
+ { AR5K_TXNOFRM, 0 },
+ { AR5K_SFR, 0 },
+ { AR5K_MIBC, 0 },
+ { AR5K_MISC, 0 },
+ { AR5K_RX_FILTER_5210, 0 },
+ { AR5K_MCAST_FILTER0_5210, 0 },
+ { AR5K_MCAST_FILTER1_5210, 0 },
+ { AR5K_TX_MASK0, 0 },
+ { AR5K_TX_MASK1, 0 },
+ { AR5K_CLR_TMASK, 0 },
+ { AR5K_TRIG_LVL, AR5K_TUNE_MIN_TX_FIFO_THRES },
+ { AR5K_DIAG_SW_5210, 0 },
+ { AR5K_RSSI_THR, AR5K_TUNE_RSSI_THRES },
+ { AR5K_TSF_L32_5210, 0 },
+ { AR5K_TIMER0_5210, 0 },
+ { AR5K_TIMER1_5210, 0xffffffff },
+ { AR5K_TIMER2_5210, 0xffffffff },
+ { AR5K_TIMER3_5210, 1 },
+ { AR5K_CFP_DUR_5210, 0 },
+ { AR5K_CFP_PERIOD_5210, 0 },
+ /* PHY registers */
+ { AR5K_PHY(0), 0x00000047 },
+ { AR5K_PHY_AGC, 0x00000000 },
+ { AR5K_PHY(3), 0x09848ea6 },
+ { AR5K_PHY(4), 0x3d32e000 },
+ { AR5K_PHY(5), 0x0000076b },
+ { AR5K_PHY_ACT, AR5K_PHY_ACT_DISABLE },
+ { AR5K_PHY(8), 0x02020200 },
+ { AR5K_PHY(9), 0x00000e0e },
+ { AR5K_PHY(10), 0x0a020201 },
+ { AR5K_PHY(11), 0x00036ffc },
+ { AR5K_PHY(12), 0x00000000 },
+ { AR5K_PHY(13), 0x00000e0e },
+ { AR5K_PHY(14), 0x00000007 },
+ { AR5K_PHY(15), 0x00020100 },
+ { AR5K_PHY(16), 0x89630000 },
+ { AR5K_PHY(17), 0x1372169c },
+ { AR5K_PHY(18), 0x0018b633 },
+ { AR5K_PHY(19), 0x1284613c },
+ { AR5K_PHY(20), 0x0de8b8e0 },
+ { AR5K_PHY(21), 0x00074859 },
+ { AR5K_PHY(22), 0x7e80beba },
+ { AR5K_PHY(23), 0x313a665e },
+ { AR5K_PHY_AGCCTL, 0x00001d08 },
+ { AR5K_PHY(25), 0x0001ce00 },
+ { AR5K_PHY(26), 0x409a4190 },
+ { AR5K_PHY(28), 0x0000000f },
+ { AR5K_PHY(29), 0x00000080 },
+ { AR5K_PHY(30), 0x00000004 },
+ { AR5K_PHY(31), 0x00000018 }, /* 0x987c */
+ { AR5K_PHY(64), 0x00000000 }, /* 0x9900 */
+ { AR5K_PHY(65), 0x00000000 },
+ { AR5K_PHY(66), 0x00000000 },
+ { AR5K_PHY(67), 0x00800000 },
+ { AR5K_PHY(68), 0x00000003 },
+ /* BB gain table (64bytes) */
+ { AR5K_BB_GAIN(0), 0x00000000 },
+ { AR5K_BB_GAIN(1), 0x00000020 },
+ { AR5K_BB_GAIN(2), 0x00000010 },
+ { AR5K_BB_GAIN(3), 0x00000030 },
+ { AR5K_BB_GAIN(4), 0x00000008 },
+ { AR5K_BB_GAIN(5), 0x00000028 },
+ { AR5K_BB_GAIN(6), 0x00000028 },
+ { AR5K_BB_GAIN(7), 0x00000004 },
+ { AR5K_BB_GAIN(8), 0x00000024 },
+ { AR5K_BB_GAIN(9), 0x00000014 },
+ { AR5K_BB_GAIN(10), 0x00000034 },
+ { AR5K_BB_GAIN(11), 0x0000000c },
+ { AR5K_BB_GAIN(12), 0x0000002c },
+ { AR5K_BB_GAIN(13), 0x00000002 },
+ { AR5K_BB_GAIN(14), 0x00000022 },
+ { AR5K_BB_GAIN(15), 0x00000012 },
+ { AR5K_BB_GAIN(16), 0x00000032 },
+ { AR5K_BB_GAIN(17), 0x0000000a },
+ { AR5K_BB_GAIN(18), 0x0000002a },
+ { AR5K_BB_GAIN(19), 0x00000001 },
+ { AR5K_BB_GAIN(20), 0x00000021 },
+ { AR5K_BB_GAIN(21), 0x00000011 },
+ { AR5K_BB_GAIN(22), 0x00000031 },
+ { AR5K_BB_GAIN(23), 0x00000009 },
+ { AR5K_BB_GAIN(24), 0x00000029 },
+ { AR5K_BB_GAIN(25), 0x00000005 },
+ { AR5K_BB_GAIN(26), 0x00000025 },
+ { AR5K_BB_GAIN(27), 0x00000015 },
+ { AR5K_BB_GAIN(28), 0x00000035 },
+ { AR5K_BB_GAIN(29), 0x0000000d },
+ { AR5K_BB_GAIN(30), 0x0000002d },
+ { AR5K_BB_GAIN(31), 0x00000003 },
+ { AR5K_BB_GAIN(32), 0x00000023 },
+ { AR5K_BB_GAIN(33), 0x00000013 },
+ { AR5K_BB_GAIN(34), 0x00000033 },
+ { AR5K_BB_GAIN(35), 0x0000000b },
+ { AR5K_BB_GAIN(36), 0x0000002b },
+ { AR5K_BB_GAIN(37), 0x00000007 },
+ { AR5K_BB_GAIN(38), 0x00000027 },
+ { AR5K_BB_GAIN(39), 0x00000017 },
+ { AR5K_BB_GAIN(40), 0x00000037 },
+ { AR5K_BB_GAIN(41), 0x0000000f },
+ { AR5K_BB_GAIN(42), 0x0000002f },
+ { AR5K_BB_GAIN(43), 0x0000002f },
+ { AR5K_BB_GAIN(44), 0x0000002f },
+ { AR5K_BB_GAIN(45), 0x0000002f },
+ { AR5K_BB_GAIN(46), 0x0000002f },
+ { AR5K_BB_GAIN(47), 0x0000002f },
+ { AR5K_BB_GAIN(48), 0x0000002f },
+ { AR5K_BB_GAIN(49), 0x0000002f },
+ { AR5K_BB_GAIN(50), 0x0000002f },
+ { AR5K_BB_GAIN(51), 0x0000002f },
+ { AR5K_BB_GAIN(52), 0x0000002f },
+ { AR5K_BB_GAIN(53), 0x0000002f },
+ { AR5K_BB_GAIN(54), 0x0000002f },
+ { AR5K_BB_GAIN(55), 0x0000002f },
+ { AR5K_BB_GAIN(56), 0x0000002f },
+ { AR5K_BB_GAIN(57), 0x0000002f },
+ { AR5K_BB_GAIN(58), 0x0000002f },
+ { AR5K_BB_GAIN(59), 0x0000002f },
+ { AR5K_BB_GAIN(60), 0x0000002f },
+ { AR5K_BB_GAIN(61), 0x0000002f },
+ { AR5K_BB_GAIN(62), 0x0000002f },
+ { AR5K_BB_GAIN(63), 0x0000002f },
+ /* 5110 RF gain table (64btes) */
+ { AR5K_RF_GAIN(0), 0x0000001d },
+ { AR5K_RF_GAIN(1), 0x0000005d },
+ { AR5K_RF_GAIN(2), 0x0000009d },
+ { AR5K_RF_GAIN(3), 0x000000dd },
+ { AR5K_RF_GAIN(4), 0x0000011d },
+ { AR5K_RF_GAIN(5), 0x00000021 },
+ { AR5K_RF_GAIN(6), 0x00000061 },
+ { AR5K_RF_GAIN(7), 0x000000a1 },
+ { AR5K_RF_GAIN(8), 0x000000e1 },
+ { AR5K_RF_GAIN(9), 0x00000031 },
+ { AR5K_RF_GAIN(10), 0x00000071 },
+ { AR5K_RF_GAIN(11), 0x000000b1 },
+ { AR5K_RF_GAIN(12), 0x0000001c },
+ { AR5K_RF_GAIN(13), 0x0000005c },
+ { AR5K_RF_GAIN(14), 0x00000029 },
+ { AR5K_RF_GAIN(15), 0x00000069 },
+ { AR5K_RF_GAIN(16), 0x000000a9 },
+ { AR5K_RF_GAIN(17), 0x00000020 },
+ { AR5K_RF_GAIN(18), 0x00000019 },
+ { AR5K_RF_GAIN(19), 0x00000059 },
+ { AR5K_RF_GAIN(20), 0x00000099 },
+ { AR5K_RF_GAIN(21), 0x00000030 },
+ { AR5K_RF_GAIN(22), 0x00000005 },
+ { AR5K_RF_GAIN(23), 0x00000025 },
+ { AR5K_RF_GAIN(24), 0x00000065 },
+ { AR5K_RF_GAIN(25), 0x000000a5 },
+ { AR5K_RF_GAIN(26), 0x00000028 },
+ { AR5K_RF_GAIN(27), 0x00000068 },
+ { AR5K_RF_GAIN(28), 0x0000001f },
+ { AR5K_RF_GAIN(29), 0x0000001e },
+ { AR5K_RF_GAIN(30), 0x00000018 },
+ { AR5K_RF_GAIN(31), 0x00000058 },
+ { AR5K_RF_GAIN(32), 0x00000098 },
+ { AR5K_RF_GAIN(33), 0x00000003 },
+ { AR5K_RF_GAIN(34), 0x00000004 },
+ { AR5K_RF_GAIN(35), 0x00000044 },
+ { AR5K_RF_GAIN(36), 0x00000084 },
+ { AR5K_RF_GAIN(37), 0x00000013 },
+ { AR5K_RF_GAIN(38), 0x00000012 },
+ { AR5K_RF_GAIN(39), 0x00000052 },
+ { AR5K_RF_GAIN(40), 0x00000092 },
+ { AR5K_RF_GAIN(41), 0x000000d2 },
+ { AR5K_RF_GAIN(42), 0x0000002b },
+ { AR5K_RF_GAIN(43), 0x0000002a },
+ { AR5K_RF_GAIN(44), 0x0000006a },
+ { AR5K_RF_GAIN(45), 0x000000aa },
+ { AR5K_RF_GAIN(46), 0x0000001b },
+ { AR5K_RF_GAIN(47), 0x0000001a },
+ { AR5K_RF_GAIN(48), 0x0000005a },
+ { AR5K_RF_GAIN(49), 0x0000009a },
+ { AR5K_RF_GAIN(50), 0x000000da },
+ { AR5K_RF_GAIN(51), 0x00000006 },
+ { AR5K_RF_GAIN(52), 0x00000006 },
+ { AR5K_RF_GAIN(53), 0x00000006 },
+ { AR5K_RF_GAIN(54), 0x00000006 },
+ { AR5K_RF_GAIN(55), 0x00000006 },
+ { AR5K_RF_GAIN(56), 0x00000006 },
+ { AR5K_RF_GAIN(57), 0x00000006 },
+ { AR5K_RF_GAIN(58), 0x00000006 },
+ { AR5K_RF_GAIN(59), 0x00000006 },
+ { AR5K_RF_GAIN(60), 0x00000006 },
+ { AR5K_RF_GAIN(61), 0x00000006 },
+ { AR5K_RF_GAIN(62), 0x00000006 },
+ { AR5K_RF_GAIN(63), 0x00000006 },
+ /* PHY activation */
+ { AR5K_PHY(53), 0x00000020 },
+ { AR5K_PHY(51), 0x00000004 },
+ { AR5K_PHY(50), 0x00060106 },
+ { AR5K_PHY(39), 0x0000006d },
+ { AR5K_PHY(48), 0x00000000 },
+ { AR5K_PHY(52), 0x00000014 },
+ { AR5K_PHY_ACT, AR5K_PHY_ACT_ENABLE },
+};
+
+/* Initial register settings for AR5211 */
+static const struct ath5k_ini ar5211_ini[] = {
+ { AR5K_RXDP, 0x00000000 },
+ { AR5K_RTSD0, 0x84849c9c },
+ { AR5K_RTSD1, 0x7c7c7c7c },
+ { AR5K_RXCFG, 0x00000005 },
+ { AR5K_MIBC, 0x00000000 },
+ { AR5K_TOPS, 0x00000008 },
+ { AR5K_RXNOFRM, 0x00000008 },
+ { AR5K_TXNOFRM, 0x00000010 },
+ { AR5K_RPGTO, 0x00000000 },
+ { AR5K_RFCNT, 0x0000001f },
+ { AR5K_QUEUE_TXDP(0), 0x00000000 },
+ { AR5K_QUEUE_TXDP(1), 0x00000000 },
+ { AR5K_QUEUE_TXDP(2), 0x00000000 },
+ { AR5K_QUEUE_TXDP(3), 0x00000000 },
+ { AR5K_QUEUE_TXDP(4), 0x00000000 },
+ { AR5K_QUEUE_TXDP(5), 0x00000000 },
+ { AR5K_QUEUE_TXDP(6), 0x00000000 },
+ { AR5K_QUEUE_TXDP(7), 0x00000000 },
+ { AR5K_QUEUE_TXDP(8), 0x00000000 },
+ { AR5K_QUEUE_TXDP(9), 0x00000000 },
+ { AR5K_DCU_FP, 0x00000000 },
+ { AR5K_STA_ID1, 0x00000000 },
+ { AR5K_BSS_ID0, 0x00000000 },
+ { AR5K_BSS_ID1, 0x00000000 },
+ { AR5K_RSSI_THR, 0x00000000 },
+ { AR5K_CFP_PERIOD_5211, 0x00000000 },
+ { AR5K_TIMER0_5211, 0x00000030 },
+ { AR5K_TIMER1_5211, 0x0007ffff },
+ { AR5K_TIMER2_5211, 0x01ffffff },
+ { AR5K_TIMER3_5211, 0x00000031 },
+ { AR5K_CFP_DUR_5211, 0x00000000 },
+ { AR5K_RX_FILTER_5211, 0x00000000 },
+ { AR5K_MCAST_FILTER0_5211, 0x00000000 },
+ { AR5K_MCAST_FILTER1_5211, 0x00000002 },
+ { AR5K_DIAG_SW_5211, 0x00000000 },
+ { AR5K_ADDAC_TEST, 0x00000000 },
+ { AR5K_DEFAULT_ANTENNA, 0x00000000 },
+ /* PHY registers */
+ { AR5K_PHY_AGC, 0x00000000 },
+ { AR5K_PHY(3), 0x2d849093 },
+ { AR5K_PHY(4), 0x7d32e000 },
+ { AR5K_PHY(5), 0x00000f6b },
+ { AR5K_PHY_ACT, 0x00000000 },
+ { AR5K_PHY(11), 0x00026ffe },
+ { AR5K_PHY(12), 0x00000000 },
+ { AR5K_PHY(15), 0x00020100 },
+ { AR5K_PHY(16), 0x206a017a },
+ { AR5K_PHY(19), 0x1284613c },
+ { AR5K_PHY(21), 0x00000859 },
+ { AR5K_PHY(26), 0x409a4190 }, /* 0x9868 */
+ { AR5K_PHY(27), 0x050cb081 },
+ { AR5K_PHY(28), 0x0000000f },
+ { AR5K_PHY(29), 0x00000080 },
+ { AR5K_PHY(30), 0x0000000c },
+ { AR5K_PHY(64), 0x00000000 },
+ { AR5K_PHY(65), 0x00000000 },
+ { AR5K_PHY(66), 0x00000000 },
+ { AR5K_PHY(67), 0x00800000 },
+ { AR5K_PHY(68), 0x00000001 },
+ { AR5K_PHY(71), 0x0000092a },
+ { AR5K_PHY_IQ, 0x00000000 },
+ { AR5K_PHY(73), 0x00058a05 },
+ { AR5K_PHY(74), 0x00000001 },
+ { AR5K_PHY(75), 0x00000000 },
+ { AR5K_PHY_PAPD_PROBE, 0x00000000 },
+ { AR5K_PHY(77), 0x00000000 }, /* 0x9934 */
+ { AR5K_PHY(78), 0x00000000 }, /* 0x9938 */
+ { AR5K_PHY(79), 0x0000003f }, /* 0x993c */
+ { AR5K_PHY(80), 0x00000004 },
+ { AR5K_PHY(82), 0x00000000 },
+ { AR5K_PHY(83), 0x00000000 },
+ { AR5K_PHY(84), 0x00000000 },
+ { AR5K_PHY_RADAR, 0x5d50f14c },
+ { AR5K_PHY(86), 0x00000018 },
+ { AR5K_PHY(87), 0x004b6a8e },
+ /* Initial Power table (32bytes)
+ * common on all cards/modes.
+ * Note: Table is rewritten during
+ * txpower setup later using calibration
+ * data etc. so next write is non-common */
+ { AR5K_PHY_PCDAC_TXPOWER(1), 0x06ff05ff },
+ { AR5K_PHY_PCDAC_TXPOWER(2), 0x07ff07ff },
+ { AR5K_PHY_PCDAC_TXPOWER(3), 0x08ff08ff },
+ { AR5K_PHY_PCDAC_TXPOWER(4), 0x09ff09ff },
+ { AR5K_PHY_PCDAC_TXPOWER(5), 0x0aff0aff },
+ { AR5K_PHY_PCDAC_TXPOWER(6), 0x0bff0bff },
+ { AR5K_PHY_PCDAC_TXPOWER(7), 0x0cff0cff },
+ { AR5K_PHY_PCDAC_TXPOWER(8), 0x0dff0dff },
+ { AR5K_PHY_PCDAC_TXPOWER(9), 0x0fff0eff },
+ { AR5K_PHY_PCDAC_TXPOWER(10), 0x12ff12ff },
+ { AR5K_PHY_PCDAC_TXPOWER(11), 0x14ff13ff },
+ { AR5K_PHY_PCDAC_TXPOWER(12), 0x16ff15ff },
+ { AR5K_PHY_PCDAC_TXPOWER(13), 0x19ff17ff },
+ { AR5K_PHY_PCDAC_TXPOWER(14), 0x1bff1aff },
+ { AR5K_PHY_PCDAC_TXPOWER(15), 0x1eff1dff },
+ { AR5K_PHY_PCDAC_TXPOWER(16), 0x23ff20ff },
+ { AR5K_PHY_PCDAC_TXPOWER(17), 0x27ff25ff },
+ { AR5K_PHY_PCDAC_TXPOWER(18), 0x2cff29ff },
+ { AR5K_PHY_PCDAC_TXPOWER(19), 0x31ff2fff },
+ { AR5K_PHY_PCDAC_TXPOWER(20), 0x37ff34ff },
+ { AR5K_PHY_PCDAC_TXPOWER(21), 0x3aff3aff },
+ { AR5K_PHY_PCDAC_TXPOWER(22), 0x3aff3aff },
+ { AR5K_PHY_PCDAC_TXPOWER(23), 0x3aff3aff },
+ { AR5K_PHY_PCDAC_TXPOWER(24), 0x3aff3aff },
+ { AR5K_PHY_PCDAC_TXPOWER(25), 0x3aff3aff },
+ { AR5K_PHY_PCDAC_TXPOWER(26), 0x3aff3aff },
+ { AR5K_PHY_PCDAC_TXPOWER(27), 0x3aff3aff },
+ { AR5K_PHY_PCDAC_TXPOWER(28), 0x3aff3aff },
+ { AR5K_PHY_PCDAC_TXPOWER(29), 0x3aff3aff },
+ { AR5K_PHY_PCDAC_TXPOWER(30), 0x3aff3aff },
+ { AR5K_PHY_PCDAC_TXPOWER(31), 0x3aff3aff },
+ { AR5K_PHY_CCKTXCTL, 0x00000000 },
+ { AR5K_PHY(642), 0x503e4646 },
+ { AR5K_PHY_GAIN_2GHZ, 0x6480416c },
+ { AR5K_PHY(644), 0x0199a003 },
+ { AR5K_PHY(645), 0x044cd610 },
+ { AR5K_PHY(646), 0x13800040 },
+ { AR5K_PHY(647), 0x1be00060 },
+ { AR5K_PHY(648), 0x0c53800a },
+ { AR5K_PHY(649), 0x0014df3b },
+ { AR5K_PHY(650), 0x000001b5 },
+ { AR5K_PHY(651), 0x00000020 },
+};
+
+/* Initial mode-specific settings for AR5211
+ * 5211 supports OFDM-only g (draft g) but we
+ * need to test it !
+ */
+static const struct ath5k_ini_mode ar5211_ini_mode[] = {
+ { AR5K_TXCFG,
+ /* A/XR B G */
+ { 0x00000015, 0x0000001d, 0x00000015 } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(0),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(1),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(2),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(3),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(4),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(5),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(6),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(7),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(8),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(9),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_DCU_GBL_IFS_SLOT,
+ { 0x00000168, 0x000001b8, 0x00000168 } },
+ { AR5K_DCU_GBL_IFS_SIFS,
+ { 0x00000230, 0x000000b0, 0x00000230 } },
+ { AR5K_DCU_GBL_IFS_EIFS,
+ { 0x00000d98, 0x00001f48, 0x00000d98 } },
+ { AR5K_DCU_GBL_IFS_MISC,
+ { 0x0000a0e0, 0x00005880, 0x0000a0e0 } },
+ { AR5K_TIME_OUT,
+ { 0x04000400, 0x20003000, 0x04000400 } },
+ { AR5K_USEC_5211,
+ { 0x0e8d8fa7, 0x01608f95, 0x0e8d8fa7 } },
+ { AR5K_PHY(8),
+ { 0x02020200, 0x02010200, 0x02020200 } },
+ { AR5K_PHY_RF_CTL2,
+ { 0x00000e0e, 0x00000707, 0x00000e0e } },
+ { AR5K_PHY_RF_CTL3,
+ { 0x0a020001, 0x05010000, 0x0a020001 } },
+ { AR5K_PHY_RF_CTL4,
+ { 0x00000e0e, 0x00000e0e, 0x00000e0e } },
+ { AR5K_PHY_PA_CTL,
+ { 0x00000007, 0x0000000b, 0x0000000b } },
+ { AR5K_PHY_SETTLING,
+ { 0x1372169c, 0x137216a8, 0x1372169c } },
+ { AR5K_PHY_GAIN,
+ { 0x0018ba67, 0x0018ba69, 0x0018ba69 } },
+ { AR5K_PHY_DESIRED_SIZE,
+ { 0x0c28b4e0, 0x0c28b4e0, 0x0c28b4e0 } },
+ { AR5K_PHY_SIG,
+ { 0x7e800d2e, 0x7ec00d2e, 0x7e800d2e } },
+ { AR5K_PHY_AGCCOARSE,
+ { 0x31375d5e, 0x313a5d5e, 0x31375d5e } },
+ { AR5K_PHY_AGCCTL,
+ { 0x0000bd10, 0x0000bd38, 0x0000bd10 } },
+ { AR5K_PHY_NF,
+ { 0x0001ce00, 0x0001ce00, 0x0001ce00 } },
+ { AR5K_PHY_RX_DELAY,
+ { 0x00002710, 0x0000157c, 0x00002710 } },
+ { AR5K_PHY(70),
+ { 0x00000190, 0x00000084, 0x00000190 } },
+ { AR5K_PHY_FRAME_CTL_5211,
+ { 0x6fe01020, 0x6fe00920, 0x6fe01020 } },
+ { AR5K_PHY_PCDAC_TXPOWER_BASE,
+ { 0x05ff14ff, 0x05ff14ff, 0x05ff19ff } },
+ { AR5K_RF_BUFFER_CONTROL_4,
+ { 0x00000010, 0x00000010, 0x00000010 } },
+};
+
+/* Initial register settings for AR5212 */
+static const struct ath5k_ini ar5212_ini_common_start[] = {
+ { AR5K_RXDP, 0x00000000 },
+ { AR5K_RXCFG, 0x00000005 },
+ { AR5K_MIBC, 0x00000000 },
+ { AR5K_TOPS, 0x00000008 },
+ { AR5K_RXNOFRM, 0x00000008 },
+ { AR5K_TXNOFRM, 0x00000010 },
+ { AR5K_RPGTO, 0x00000000 },
+ { AR5K_RFCNT, 0x0000001f },
+ { AR5K_QUEUE_TXDP(0), 0x00000000 },
+ { AR5K_QUEUE_TXDP(1), 0x00000000 },
+ { AR5K_QUEUE_TXDP(2), 0x00000000 },
+ { AR5K_QUEUE_TXDP(3), 0x00000000 },
+ { AR5K_QUEUE_TXDP(4), 0x00000000 },
+ { AR5K_QUEUE_TXDP(5), 0x00000000 },
+ { AR5K_QUEUE_TXDP(6), 0x00000000 },
+ { AR5K_QUEUE_TXDP(7), 0x00000000 },
+ { AR5K_QUEUE_TXDP(8), 0x00000000 },
+ { AR5K_QUEUE_TXDP(9), 0x00000000 },
+ { AR5K_DCU_FP, 0x00000000 },
+ { AR5K_DCU_TXP, 0x00000000 },
+ /* Tx filter table 0 (32 entries) */
+ { AR5K_DCU_TX_FILTER_0(0), 0x00000000 }, /* DCU 0 */
+ { AR5K_DCU_TX_FILTER_0(1), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(2), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(3), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(4), 0x00000000 }, /* DCU 1 */
+ { AR5K_DCU_TX_FILTER_0(5), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(6), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(7), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(8), 0x00000000 }, /* DCU 2 */
+ { AR5K_DCU_TX_FILTER_0(9), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(10), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(11), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(12), 0x00000000 }, /* DCU 3 */
+ { AR5K_DCU_TX_FILTER_0(13), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(14), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(15), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(16), 0x00000000 }, /* DCU 4 */
+ { AR5K_DCU_TX_FILTER_0(17), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(18), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(19), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(20), 0x00000000 }, /* DCU 5 */
+ { AR5K_DCU_TX_FILTER_0(21), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(22), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(23), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(24), 0x00000000 }, /* DCU 6 */
+ { AR5K_DCU_TX_FILTER_0(25), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(26), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(27), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(28), 0x00000000 }, /* DCU 7 */
+ { AR5K_DCU_TX_FILTER_0(29), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(30), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_0(31), 0x00000000 },
+ /* Tx filter table 1 (16 entries) */
+ { AR5K_DCU_TX_FILTER_1(0), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_1(1), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_1(2), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_1(3), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_1(4), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_1(5), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_1(6), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_1(7), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_1(8), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_1(9), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_1(10), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_1(11), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_1(12), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_1(13), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_1(14), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_1(15), 0x00000000 },
+ { AR5K_DCU_TX_FILTER_CLR, 0x00000000 },
+ { AR5K_DCU_TX_FILTER_SET, 0x00000000 },
+ { AR5K_STA_ID1, 0x00000000 },
+ { AR5K_BSS_ID0, 0x00000000 },
+ { AR5K_BSS_ID1, 0x00000000 },
+ { AR5K_BEACON_5211, 0x00000000 },
+ { AR5K_CFP_PERIOD_5211, 0x00000000 },
+ { AR5K_TIMER0_5211, 0x00000030 },
+ { AR5K_TIMER1_5211, 0x0007ffff },
+ { AR5K_TIMER2_5211, 0x01ffffff },
+ { AR5K_TIMER3_5211, 0x00000031 },
+ { AR5K_CFP_DUR_5211, 0x00000000 },
+ { AR5K_RX_FILTER_5211, 0x00000000 },
+ { AR5K_DIAG_SW_5211, 0x00000000 },
+ { AR5K_ADDAC_TEST, 0x00000000 },
+ { AR5K_DEFAULT_ANTENNA, 0x00000000 },
+ { AR5K_FRAME_CTL_QOSM, 0x000fc78f },
+ { AR5K_XRMODE, 0x2a82301a },
+ { AR5K_XRDELAY, 0x05dc01e0 },
+ { AR5K_XRTIMEOUT, 0x1f402710 },
+ { AR5K_XRCHIRP, 0x01f40000 },
+ { AR5K_XRSTOMP, 0x00001e1c },
+ { AR5K_SLEEP0, 0x0002aaaa },
+ { AR5K_SLEEP1, 0x02005555 },
+ { AR5K_SLEEP2, 0x00000000 },
+ { AR_BSSMSKL, 0xffffffff },
+ { AR_BSSMSKU, 0x0000ffff },
+ { AR5K_TXPC, 0x00000000 },
+ { AR5K_PROFCNT_TX, 0x00000000 },
+ { AR5K_PROFCNT_RX, 0x00000000 },
+ { AR5K_PROFCNT_RXCLR, 0x00000000 },
+ { AR5K_PROFCNT_CYCLE, 0x00000000 },
+ { AR5K_QUIET_CTL1, 0x00000088 },
+ /* Initial rate duration table (32 entries )*/
+ { AR5K_RATE_DUR(0), 0x00000000 },
+ { AR5K_RATE_DUR(1), 0x0000008c },
+ { AR5K_RATE_DUR(2), 0x000000e4 },
+ { AR5K_RATE_DUR(3), 0x000002d5 },
+ { AR5K_RATE_DUR(4), 0x00000000 },
+ { AR5K_RATE_DUR(5), 0x00000000 },
+ { AR5K_RATE_DUR(6), 0x000000a0 },
+ { AR5K_RATE_DUR(7), 0x000001c9 },
+ { AR5K_RATE_DUR(8), 0x0000002c },
+ { AR5K_RATE_DUR(9), 0x0000002c },
+ { AR5K_RATE_DUR(10), 0x00000030 },
+ { AR5K_RATE_DUR(11), 0x0000003c },
+ { AR5K_RATE_DUR(12), 0x0000002c },
+ { AR5K_RATE_DUR(13), 0x0000002c },
+ { AR5K_RATE_DUR(14), 0x00000030 },
+ { AR5K_RATE_DUR(15), 0x0000003c },
+ { AR5K_RATE_DUR(16), 0x00000000 },
+ { AR5K_RATE_DUR(17), 0x00000000 },
+ { AR5K_RATE_DUR(18), 0x00000000 },
+ { AR5K_RATE_DUR(19), 0x00000000 },
+ { AR5K_RATE_DUR(20), 0x00000000 },
+ { AR5K_RATE_DUR(21), 0x00000000 },
+ { AR5K_RATE_DUR(22), 0x00000000 },
+ { AR5K_RATE_DUR(23), 0x00000000 },
+ { AR5K_RATE_DUR(24), 0x000000d5 },
+ { AR5K_RATE_DUR(25), 0x000000df },
+ { AR5K_RATE_DUR(26), 0x00000102 },
+ { AR5K_RATE_DUR(27), 0x0000013a },
+ { AR5K_RATE_DUR(28), 0x00000075 },
+ { AR5K_RATE_DUR(29), 0x0000007f },
+ { AR5K_RATE_DUR(30), 0x000000a2 },
+ { AR5K_RATE_DUR(31), 0x00000000 },
+ { AR5K_QUIET_CTL2, 0x00010002 },
+ { AR5K_TSF_PARM, 0x00000001 },
+ { AR5K_QOS_NOACK, 0x000000c0 },
+ { AR5K_PHY_ERR_FIL, 0x00000000 },
+ { AR5K_XRLAT_TX, 0x00000168 },
+ { AR5K_ACKSIFS, 0x00000000 },
+ /* Rate -> db table
+ * notice ...03<-02<-01<-00 ! */
+ { AR5K_RATE2DB(0), 0x03020100 },
+ { AR5K_RATE2DB(1), 0x07060504 },
+ { AR5K_RATE2DB(2), 0x0b0a0908 },
+ { AR5K_RATE2DB(3), 0x0f0e0d0c },
+ { AR5K_RATE2DB(4), 0x13121110 },
+ { AR5K_RATE2DB(5), 0x17161514 },
+ { AR5K_RATE2DB(6), 0x1b1a1918 },
+ { AR5K_RATE2DB(7), 0x1f1e1d1c },
+ /* Db -> Rate table */
+ { AR5K_DB2RATE(0), 0x03020100 },
+ { AR5K_DB2RATE(1), 0x07060504 },
+ { AR5K_DB2RATE(2), 0x0b0a0908 },
+ { AR5K_DB2RATE(3), 0x0f0e0d0c },
+ { AR5K_DB2RATE(4), 0x13121110 },
+ { AR5K_DB2RATE(5), 0x17161514 },
+ { AR5K_DB2RATE(6), 0x1b1a1918 },
+ { AR5K_DB2RATE(7), 0x1f1e1d1c },
+ /* PHY registers (Common settings
+ * for all chips/modes) */
+ { AR5K_PHY(3), 0xad848e19 },
+ { AR5K_PHY(4), 0x7d28e000 },
+ { AR5K_PHY_TIMING_3, 0x9c0a9f6b },
+ { AR5K_PHY_ACT, 0x00000000 },
+ { AR5K_PHY(16), 0x206a017a },
+ { AR5K_PHY(21), 0x00000859 },
+ { AR5K_PHY_BIN_MASK_1, 0x00000000 },
+ { AR5K_PHY_BIN_MASK_2, 0x00000000 },
+ { AR5K_PHY_BIN_MASK_3, 0x00000000 },
+ { AR5K_PHY_BIN_MASK_CTL, 0x00800000 },
+ { AR5K_PHY_ANT_CTL, 0x00000001 },
+ /*{ AR5K_PHY(71), 0x0000092a },*/ /* Old value */
+ { AR5K_PHY_MAX_RX_LEN, 0x00000c80 },
+ { AR5K_PHY_IQ, 0x05100000 },
+ { AR5K_PHY_WARM_RESET, 0x00000001 },
+ { AR5K_PHY_CTL, 0x00000004 },
+ { AR5K_PHY_TXPOWER_RATE1, 0x1e1f2022 },
+ { AR5K_PHY_TXPOWER_RATE2, 0x0a0b0c0d },
+ { AR5K_PHY_TXPOWER_RATE_MAX, 0x0000003f },
+ { AR5K_PHY(82), 0x9280b212 },
+ { AR5K_PHY_RADAR, 0x5d50e188 },
+ /*{ AR5K_PHY(86), 0x000000ff },*/
+ { AR5K_PHY(87), 0x004b6a8e },
+ { AR5K_PHY_NFTHRES, 0x000003ce },
+ { AR5K_PHY_RESTART, 0x192fb515 },
+ { AR5K_PHY(94), 0x00000001 },
+ { AR5K_PHY_RFBUS_REQ, 0x00000000 },
+ /*{ AR5K_PHY(644), 0x0080a333 },*/ /* Old value */
+ /*{ AR5K_PHY(645), 0x00206c10 },*/ /* Old value */
+ { AR5K_PHY(644), 0x00806333 },
+ { AR5K_PHY(645), 0x00106c10 },
+ { AR5K_PHY(646), 0x009c4060 },
+ /* { AR5K_PHY(647), 0x1483800a }, */
+ /* { AR5K_PHY(648), 0x01831061 }, */ /* Old value */
+ { AR5K_PHY(648), 0x018830c6 },
+ { AR5K_PHY(649), 0x00000400 },
+ /*{ AR5K_PHY(650), 0x000001b5 },*/
+ { AR5K_PHY(651), 0x00000000 },
+ { AR5K_PHY_TXPOWER_RATE3, 0x20202020 },
+ { AR5K_PHY_TXPOWER_RATE4, 0x20202020 },
+ /*{ AR5K_PHY(655), 0x13c889af },*/
+ { AR5K_PHY(656), 0x38490a20 },
+ { AR5K_PHY(657), 0x00007bb6 },
+ { AR5K_PHY(658), 0x0fff3ffc },
+};
+
+/* Initial mode-specific settings for AR5212 (Written before ar5212_ini) */
+static const struct ath5k_ini_mode ar5212_ini_mode_start[] = {
+ { AR5K_QUEUE_DFS_LOCAL_IFS(0),
+ /* A/XR B G */
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(1),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(2),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(3),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(4),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(5),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(6),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(7),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(8),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_QUEUE_DFS_LOCAL_IFS(9),
+ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+ { AR5K_DCU_GBL_IFS_SIFS,
+ { 0x00000230, 0x000000b0, 0x00000160 } },
+ { AR5K_DCU_GBL_IFS_SLOT,
+ { 0x00000168, 0x000001b8, 0x0000018c } },
+ { AR5K_DCU_GBL_IFS_EIFS,
+ { 0x00000e60, 0x00001f1c, 0x00003e38 } },
+ { AR5K_DCU_GBL_IFS_MISC,
+ { 0x0000a0e0, 0x00005880, 0x0000b0e0 } },
+ { AR5K_TIME_OUT,
+ { 0x03e803e8, 0x04200420, 0x08400840 } },
+ { AR5K_PHY(8),
+ { 0x02020200, 0x02010200, 0x02020200 } },
+ { AR5K_PHY_RF_CTL2,
+ { 0x00000e0e, 0x00000707, 0x00000e0e } },
+ { AR5K_PHY_SETTLING,
+ { 0x1372161c, 0x13721722, 0x137216a2 } },
+ { AR5K_PHY_AGCCTL,
+ { 0x00009d10, 0x00009d18, 0x00009d18 } },
+ { AR5K_PHY_NF,
+ { 0x0001ce00, 0x0001ce00, 0x0001ce00 } },
+ { AR5K_PHY_WEAK_OFDM_HIGH_THR,
+ { 0x409a4190, 0x409a4190, 0x409a4190 } },
+ { AR5K_PHY(70),
+ { 0x000001b8, 0x00000084, 0x00000108 } },
+ { AR5K_PHY_OFDM_SELFCORR,
+ { 0x10058a05, 0x10058a05, 0x10058a05 } },
+ { 0xa230,
+ { 0x00000000, 0x00000000, 0x00000108 } },
+};
+
+/* Initial mode-specific settings for AR5212 + RF5111 (Written after ar5212_ini) */
+static const struct ath5k_ini_mode rf5111_ini_mode_end[] = {
+ { AR5K_TXCFG,
+ /* A/XR B G */
+ { 0x00008015, 0x00008015, 0x00008015 } },
+ { AR5K_USEC_5211,
+ { 0x128d8fa7, 0x04e00f95, 0x12e00fab } },
+ { AR5K_PHY_RF_CTL3,
+ { 0x0a020001, 0x05010100, 0x0a020001 } },
+ { AR5K_PHY_RF_CTL4,
+ { 0x00000e0e, 0x00000e0e, 0x00000e0e } },
+ { AR5K_PHY_PA_CTL,
+ { 0x00000007, 0x0000000b, 0x0000000b } },
+ { AR5K_PHY_GAIN,
+ { 0x0018da5a, 0x0018ca69, 0x0018ca69 } },
+ { AR5K_PHY_DESIRED_SIZE,
+ { 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0 } },
+ { AR5K_PHY_SIG,
+ { 0x7e800d2e, 0x7ee84d2e, 0x7ee84d2e } },
+ { AR5K_PHY_AGCCOARSE,
+ { 0x3137665e, 0x3137665e, 0x3137665e } },
+ { AR5K_PHY_WEAK_OFDM_LOW_THR,
+ { 0x050cb081, 0x050cb081, 0x050cb080 } },
+ { AR5K_PHY_RX_DELAY,
+ { 0x00002710, 0x0000157c, 0x00002af8 } },
+ { AR5K_PHY_FRAME_CTL_5211,
+ { 0xf7b81020, 0xf7b80d20, 0xf7b81020 } },
+ { AR5K_PHY_GAIN_2GHZ,
+ { 0x642c416a, 0x6440416a, 0x6440416a } },
+ { AR5K_PHY_CCK_RX_CTL_4,
+ { 0x1883800a, 0x1873800a, 0x1883800a } },
+};
+
+static const struct ath5k_ini rf5111_ini_common_end[] = {
+ { AR5K_DCU_FP, 0x00000000 },
+ { AR5K_PHY_AGC, 0x00000000 },
+ { AR5K_PHY_ADC_CTL, 0x00022ffe },
+ { 0x983c, 0x00020100 },
+ { AR5K_PHY_GAIN_OFFSET, 0x1284613c },
+ { AR5K_PHY_PAPD_PROBE, 0x00004883 },
+ { 0x9940, 0x00000004 },
+ { 0x9958, 0x000000ff },
+ { 0x9974, 0x00000000 },
+ { AR5K_PHY_SPENDING, 0x00000018 },
+ { AR5K_PHY_CCKTXCTL, 0x00000000 },
+ { AR5K_PHY_CCK_CROSSCORR, 0xd03e6788 },
+ { AR5K_PHY_DAG_CCK_CTL, 0x000001b5 },
+ { 0xa23c, 0x13c889af },
+};
+
+/* Initial mode-specific settings for AR5212 + RF5112 (Written after ar5212_ini) */
+static const struct ath5k_ini_mode rf5112_ini_mode_end[] = {
+ { AR5K_TXCFG,
+ /* A/XR B G */
+ { 0x00008015, 0x00008015, 0x00008015 } },
+ { AR5K_USEC_5211,
+ { 0x128d93a7, 0x04e01395, 0x12e013ab } },
+ { AR5K_PHY_RF_CTL3,
+ { 0x0a020001, 0x05020100, 0x0a020001 } },
+ { AR5K_PHY_RF_CTL4,
+ { 0x00000e0e, 0x00000e0e, 0x00000e0e } },
+ { AR5K_PHY_PA_CTL,
+ { 0x00000007, 0x0000000b, 0x0000000b } },
+ { AR5K_PHY_GAIN,
+ { 0x0018da6d, 0x0018ca75, 0x0018ca75 } },
+ { AR5K_PHY_DESIRED_SIZE,
+ { 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0 } },
+ { AR5K_PHY_SIG,
+ { 0x7e800d2e, 0x7ee80d2e, 0x7ee80d2e } },
+ { AR5K_PHY_AGCCOARSE,
+ { 0x3137665e, 0x3137665e, 0x3137665e } },
+ { AR5K_PHY_WEAK_OFDM_LOW_THR,
+ { 0x050cb081, 0x050cb081, 0x050cb081 } },
+ { AR5K_PHY_RX_DELAY,
+ { 0x000007d0, 0x0000044c, 0x00000898 } },
+ { AR5K_PHY_FRAME_CTL_5211,
+ { 0xf7b81020, 0xf7b80d10, 0xf7b81010 } },
+ { AR5K_PHY_CCKTXCTL,
+ { 0x00000000, 0x00000008, 0x00000008 } },
+ { AR5K_PHY_CCK_CROSSCORR,
+ { 0xd6be6788, 0xd03e6788, 0xd03e6788 } },
+ { AR5K_PHY_GAIN_2GHZ,
+ { 0x642c0140, 0x6442c160, 0x6442c160 } },
+ { AR5K_PHY_CCK_RX_CTL_4,
+ { 0x1883800a, 0x1873800a, 0x1883800a } },
+};
+
+static const struct ath5k_ini rf5112_ini_common_end[] = {
+ { AR5K_DCU_FP, 0x00000000 },
+ { AR5K_PHY_AGC, 0x00000000 },
+ { AR5K_PHY_ADC_CTL, 0x00022ffe },
+ { 0x983c, 0x00020100 },
+ { AR5K_PHY_GAIN_OFFSET, 0x1284613c },
+ { AR5K_PHY_PAPD_PROBE, 0x00004882 },
+ { 0x9940, 0x00000004 },
+ { 0x9958, 0x000000ff },
+ { 0x9974, 0x00000000 },
+ { AR5K_PHY_DAG_CCK_CTL, 0x000001b5 },
+ { 0xa23c, 0x13c889af },
+};
+
+/* Initial mode-specific settings for RF5413/5414 (Written after ar5212_ini) */
+static const struct ath5k_ini_mode rf5413_ini_mode_end[] = {
+ { AR5K_TXCFG,
+ /* A/XR B G */
+ { 0x00000015, 0x00000015, 0x00000015 } },
+ { AR5K_USEC_5211,
+ { 0x128d93a7, 0x04e01395, 0x12e013ab } },
+ { AR5K_PHY_RF_CTL3,
+ { 0x0a020001, 0x05020100, 0x0a020001 } },
+ { AR5K_PHY_RF_CTL4,
+ { 0x00000e0e, 0x00000e0e, 0x00000e0e } },
+ { AR5K_PHY_PA_CTL,
+ { 0x00000007, 0x0000000b, 0x0000000b } },
+ { AR5K_PHY_GAIN,
+ { 0x0018fa61, 0x001a1a63, 0x001a1a63 } },
+ { AR5K_PHY_DESIRED_SIZE,
+ { 0x0c98b4e0, 0x0c98b0da, 0x0c98b0da } },
+ { AR5K_PHY_SIG,
+ { 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e } },
+ { AR5K_PHY_AGCCOARSE,
+ { 0x3139605e, 0x3139605e, 0x3139605e } },
+ { AR5K_PHY_WEAK_OFDM_LOW_THR,
+ { 0x050cb081, 0x050cb081, 0x050cb081 } },
+ { AR5K_PHY_RX_DELAY,
+ { 0x000007d0, 0x0000044c, 0x00000898 } },
+ { AR5K_PHY_FRAME_CTL_5211,
+ { 0xf7b81000, 0xf7b80d00, 0xf7b81000 } },
+ { AR5K_PHY_CCKTXCTL,
+ { 0x00000000, 0x00000000, 0x00000000 } },
+ { AR5K_PHY_CCK_CROSSCORR,
+ { 0xd6be6788, 0xd03e6788, 0xd03e6788 } },
+ { AR5K_PHY_GAIN_2GHZ,
+ { 0x002ec1e0, 0x002ac120, 0x002ac120 } },
+ { AR5K_PHY_CCK_RX_CTL_4,
+ { 0x1883800a, 0x1863800a, 0x1883800a } },
+ { 0xa300,
+ { 0x18010000, 0x18010000, 0x18010000 } },
+ { 0xa304,
+ { 0x30032602, 0x30032602, 0x30032602 } },
+ { 0xa308,
+ { 0x48073e06, 0x48073e06, 0x48073e06 } },
+ { 0xa30c,
+ { 0x560b4c0a, 0x560b4c0a, 0x560b4c0a } },
+ { 0xa310,
+ { 0x641a600f, 0x641a600f, 0x641a600f } },
+ { 0xa314,
+ { 0x784f6e1b, 0x784f6e1b, 0x784f6e1b } },
+ { 0xa318,
+ { 0x868f7c5a, 0x868f7c5a, 0x868f7c5a } },
+ { 0xa31c,
+ { 0x90cf865b, 0x8ecf865b, 0x8ecf865b } },
+ { 0xa320,
+ { 0x9d4f970f, 0x9b4f970f, 0x9b4f970f } },
+ { 0xa324,
+ { 0xa7cfa38f, 0xa3cf9f8f, 0xa3cf9f8f } },
+ { 0xa328,
+ { 0xb55faf1f, 0xb35faf1f, 0xb35faf1f } },
+ { 0xa32c,
+ { 0xbddfb99f, 0xbbdfb99f, 0xbbdfb99f } },
+ { 0xa330,
+ { 0xcb7fc53f, 0xcb7fc73f, 0xcb7fc73f } },
+ { 0xa334,
+ { 0xd5ffd1bf, 0xd3ffd1bf, 0xd3ffd1bf } },
+};
+
+static const struct ath5k_ini rf5413_ini_common_end[] = {
+ { AR5K_DCU_FP, 0x000003e0 },
+ { AR5K_5414_CBCFG, 0x00000010 },
+ { AR5K_SEQ_MASK, 0x0000000f },
+ { 0x809c, 0x00000000 },
+ { 0x80a0, 0x00000000 },
+ { AR5K_MIC_QOS_CTL, 0x00000000 },
+ { AR5K_MIC_QOS_SEL, 0x00000000 },
+ { AR5K_MISC_MODE, 0x00000000 },
+ { AR5K_OFDM_FIL_CNT, 0x00000000 },
+ { AR5K_CCK_FIL_CNT, 0x00000000 },
+ { AR5K_PHYERR_CNT1, 0x00000000 },
+ { AR5K_PHYERR_CNT1_MASK, 0x00000000 },
+ { AR5K_PHYERR_CNT2, 0x00000000 },
+ { AR5K_PHYERR_CNT2_MASK, 0x00000000 },
+ { AR5K_TSF_THRES, 0x00000000 },
+ { 0x8140, 0x800003f9 },
+ { 0x8144, 0x00000000 },
+ { AR5K_PHY_AGC, 0x00000000 },
+ { AR5K_PHY_ADC_CTL, 0x0000a000 },
+ { 0x983c, 0x00200400 },
+ { AR5K_PHY_GAIN_OFFSET, 0x1284233c },
+ { AR5K_PHY_SCR, 0x0000001f },
+ { AR5K_PHY_SLMT, 0x00000080 },
+ { AR5K_PHY_SCAL, 0x0000000e },
+ { 0x9958, 0x00081fff },
+ { AR5K_PHY_TIMING_7, 0x00000000 },
+ { AR5K_PHY_TIMING_8, 0x02800000 },
+ { AR5K_PHY_TIMING_11, 0x00000000 },
+ { AR5K_PHY_HEAVY_CLIP_ENABLE, 0x00000000 },
+ { 0x99e4, 0xaaaaaaaa },
+ { 0x99e8, 0x3c466478 },
+ { 0x99ec, 0x000000aa },
+ { AR5K_PHY_SCLOCK, 0x0000000c },
+ { AR5K_PHY_SDELAY, 0x000000ff },
+ { AR5K_PHY_SPENDING, 0x00000014 },
+ { AR5K_PHY_DAG_CCK_CTL, 0x000009b5 },
+ { 0xa23c, 0x93c889af },
+ { AR5K_PHY_FAST_ADC, 0x00000001 },
+ { 0xa250, 0x0000a000 },
+ { AR5K_PHY_BLUETOOTH, 0x00000000 },
+ { AR5K_PHY_TPC_RG1, 0x0cc75380 },
+ { 0xa25c, 0x0f0f0f01 },
+ { 0xa260, 0x5f690f01 },
+ { 0xa264, 0x00418a11 },
+ { 0xa268, 0x00000000 },
+ { AR5K_PHY_TPC_RG5, 0x0c30c16a },
+ { 0xa270, 0x00820820 },
+ { 0xa274, 0x081b7caa },
+ { 0xa278, 0x1ce739ce },
+ { 0xa27c, 0x051701ce },
+ { 0xa338, 0x00000000 },
+ { 0xa33c, 0x00000000 },
+ { 0xa340, 0x00000000 },
+ { 0xa344, 0x00000000 },
+ { 0xa348, 0x3fffffff },
+ { 0xa34c, 0x3fffffff },
+ { 0xa350, 0x3fffffff },
+ { 0xa354, 0x0003ffff },
+ { 0xa358, 0x79a8aa1f },
+ { 0xa35c, 0x066c420f },
+ { 0xa360, 0x0f282207 },
+ { 0xa364, 0x17601685 },
+ { 0xa368, 0x1f801104 },
+ { 0xa36c, 0x37a00c03 },
+ { 0xa370, 0x3fc40883 },
+ { 0xa374, 0x57c00803 },
+ { 0xa378, 0x5fd80682 },
+ { 0xa37c, 0x7fe00482 },
+ { 0xa380, 0x7f3c7bba },
+ { 0xa384, 0xf3307ff0 },
+};
+
+/* Initial mode-specific settings for RF2413/2414 (Written after ar5212_ini) */
+/* XXX: a mode ? */
+static const struct ath5k_ini_mode rf2413_ini_mode_end[] = {
+ { AR5K_TXCFG,
+ /* A/XR B G */
+ { 0x00000015, 0x00000015, 0x00000015 } },
+ { AR5K_USEC_5211,
+ { 0x128d93a7, 0x04e01395, 0x12e013ab } },
+ { AR5K_PHY_RF_CTL3,
+ { 0x0a020001, 0x05020000, 0x0a020001 } },
+ { AR5K_PHY_RF_CTL4,
+ { 0x00000e00, 0x00000e00, 0x00000e00 } },
+ { AR5K_PHY_PA_CTL,
+ { 0x00000002, 0x0000000a, 0x0000000a } },
+ { AR5K_PHY_GAIN,
+ { 0x0018da6d, 0x001a6a64, 0x001a6a64 } },
+ { AR5K_PHY_DESIRED_SIZE,
+ { 0x0de8b4e0, 0x0de8b0da, 0x0c98b0da } },
+ { AR5K_PHY_SIG,
+ { 0x7e800d2e, 0x7ee80d2e, 0x7ec80d2e } },
+ { AR5K_PHY_AGCCOARSE,
+ { 0x3137665e, 0x3137665e, 0x3139605e } },
+ { AR5K_PHY_WEAK_OFDM_LOW_THR,
+ { 0x050cb081, 0x050cb081, 0x050cb081 } },
+ { AR5K_PHY_RX_DELAY,
+ { 0x000007d0, 0x0000044c, 0x00000898 } },
+ { AR5K_PHY_FRAME_CTL_5211,
+ { 0xf7b81000, 0xf7b80d00, 0xf7b81000 } },
+ { AR5K_PHY_CCKTXCTL,
+ { 0x00000000, 0x00000000, 0x00000000 } },
+ { AR5K_PHY_CCK_CROSSCORR,
+ { 0xd6be6788, 0xd03e6788, 0xd03e6788 } },
+ { AR5K_PHY_GAIN_2GHZ,
+ { 0x002c0140, 0x0042c140, 0x0042c140 } },
+ { AR5K_PHY_CCK_RX_CTL_4,
+ { 0x1883800a, 0x1863800a, 0x1883800a } },
+};
+
+static const struct ath5k_ini rf2413_ini_common_end[] = {
+ { AR5K_DCU_FP, 0x000003e0 },
+ { AR5K_SEQ_MASK, 0x0000000f },
+ { AR5K_MIC_QOS_CTL, 0x00000000 },
+ { AR5K_MIC_QOS_SEL, 0x00000000 },
+ { AR5K_MISC_MODE, 0x00000000 },
+ { AR5K_OFDM_FIL_CNT, 0x00000000 },
+ { AR5K_CCK_FIL_CNT, 0x00000000 },
+ { AR5K_PHYERR_CNT1, 0x00000000 },
+ { AR5K_PHYERR_CNT1_MASK, 0x00000000 },
+ { AR5K_PHYERR_CNT2, 0x00000000 },
+ { AR5K_PHYERR_CNT2_MASK, 0x00000000 },
+ { AR5K_TSF_THRES, 0x00000000 },
+ { 0x8140, 0x800000a8 },
+ { 0x8144, 0x00000000 },
+ { AR5K_PHY_AGC, 0x00000000 },
+ { AR5K_PHY_ADC_CTL, 0x0000a000 },
+ { 0x983c, 0x00200400 },
+ { AR5K_PHY_GAIN_OFFSET, 0x1284233c },
+ { AR5K_PHY_SCR, 0x0000001f },
+ { AR5K_PHY_SLMT, 0x00000080 },
+ { AR5K_PHY_SCAL, 0x0000000e },
+ { 0x9958, 0x000000ff },
+ { AR5K_PHY_TIMING_7, 0x00000000 },
+ { AR5K_PHY_TIMING_8, 0x02800000 },
+ { AR5K_PHY_TIMING_11, 0x00000000 },
+ { AR5K_PHY_HEAVY_CLIP_ENABLE, 0x00000000 },
+ { 0x99e4, 0xaaaaaaaa },
+ { 0x99e8, 0x3c466478 },
+ { 0x99ec, 0x000000aa },
+ { AR5K_PHY_SCLOCK, 0x0000000c },
+ { AR5K_PHY_SDELAY, 0x000000ff },
+ { AR5K_PHY_SPENDING, 0x00000014 },
+ { AR5K_PHY_DAG_CCK_CTL, 0x000009b5 },
+ { 0xa23c, 0x93c889af },
+ { AR5K_PHY_FAST_ADC, 0x00000001 },
+ { 0xa250, 0x0000a000 },
+ { AR5K_PHY_BLUETOOTH, 0x00000000 },
+ { AR5K_PHY_TPC_RG1, 0x0cc75380 },
+ { 0xa25c, 0x0f0f0f01 },
+ { 0xa260, 0x5f690f01 },
+ { 0xa264, 0x00418a11 },
+ { 0xa268, 0x00000000 },
+ { AR5K_PHY_TPC_RG5, 0x0c30c16a },
+ { 0xa270, 0x00820820 },
+ { 0xa274, 0x001b7caa },
+ { 0xa278, 0x1ce739ce },
+ { 0xa27c, 0x051701ce },
+ { 0xa300, 0x18010000 },
+ { 0xa304, 0x30032602 },
+ { 0xa308, 0x48073e06 },
+ { 0xa30c, 0x560b4c0a },
+ { 0xa310, 0x641a600f },
+ { 0xa314, 0x784f6e1b },
+ { 0xa318, 0x868f7c5a },
+ { 0xa31c, 0x8ecf865b },
+ { 0xa320, 0x9d4f970f },
+ { 0xa324, 0xa5cfa18f },
+ { 0xa328, 0xb55faf1f },
+ { 0xa32c, 0xbddfb99f },
+ { 0xa330, 0xcd7fc73f },
+ { 0xa334, 0xd5ffd1bf },
+ { 0xa338, 0x00000000 },
+ { 0xa33c, 0x00000000 },
+ { 0xa340, 0x00000000 },
+ { 0xa344, 0x00000000 },
+ { 0xa348, 0x3fffffff },
+ { 0xa34c, 0x3fffffff },
+ { 0xa350, 0x3fffffff },
+ { 0xa354, 0x0003ffff },
+ { 0xa358, 0x79a8aa1f },
+ { 0xa35c, 0x066c420f },
+ { 0xa360, 0x0f282207 },
+ { 0xa364, 0x17601685 },
+ { 0xa368, 0x1f801104 },
+ { 0xa36c, 0x37a00c03 },
+ { 0xa370, 0x3fc40883 },
+ { 0xa374, 0x57c00803 },
+ { 0xa378, 0x5fd80682 },
+ { 0xa37c, 0x7fe00482 },
+ { 0xa380, 0x7f3c7bba },
+ { 0xa384, 0xf3307ff0 },
+};
+
+/* Initial mode-specific settings for RF2425 (Written after ar5212_ini) */
+/* XXX: a mode ? */
+static const struct ath5k_ini_mode rf2425_ini_mode_end[] = {
+ { AR5K_TXCFG,
+ /* A/XR B G */
+ { 0x00000015, 0x00000015, 0x00000015 } },
+ { AR5K_USEC_5211,
+ { 0x128d93a7, 0x04e01395, 0x12e013ab } },
+ { AR5K_PHY_RF_CTL3,
+ { 0x0a020001, 0x05020100, 0x0a020001 } },
+ { AR5K_PHY_RF_CTL4,
+ { 0x00000e0e, 0x00000e0e, 0x00000e0e } },
+ { AR5K_PHY_PA_CTL,
+ { 0x00000003, 0x0000000b, 0x0000000b } },
+ { AR5K_PHY_SETTLING,
+ { 0x1372161c, 0x13721722, 0x13721422 } },
+ { AR5K_PHY_GAIN,
+ { 0x0018fa61, 0x00199a65, 0x00199a65 } },
+ { AR5K_PHY_DESIRED_SIZE,
+ { 0x0c98b4e0, 0x0c98b0da, 0x0c98b0da } },
+ { AR5K_PHY_SIG,
+ { 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e } },
+ { AR5K_PHY_AGCCOARSE,
+ { 0x3139605e, 0x3139605e, 0x3139605e } },
+ { AR5K_PHY_WEAK_OFDM_LOW_THR,
+ { 0x050cb081, 0x050cb081, 0x050cb081 } },
+ { AR5K_PHY_RX_DELAY,
+ { 0x000007d0, 0x0000044c, 0x00000898 } },
+ { AR5K_PHY_FRAME_CTL_5211,
+ { 0xf7b81000, 0xf7b80d00, 0xf7b81000 } },
+ { AR5K_PHY_CCKTXCTL,
+ { 0x00000000, 0x00000000, 0x00000000 } },
+ { AR5K_PHY_CCK_CROSSCORR,
+ { 0xd6be6788, 0xd03e6788, 0xd03e6788 } },
+ { AR5K_PHY_GAIN_2GHZ,
+ { 0x00000140, 0x0052c140, 0x0052c140 } },
+ { AR5K_PHY_CCK_RX_CTL_4,
+ { 0x1883800a, 0x1863800a, 0x1883800a } },
+ { 0xa324,
+ { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } },
+ { 0xa328,
+ { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } },
+ { 0xa32c,
+ { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } },
+ { 0xa330,
+ { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } },
+ { 0xa334,
+ { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } },
+};
+
+static const struct ath5k_ini rf2425_ini_common_end[] = {
+ { AR5K_DCU_FP, 0x000003e0 },
+ { AR5K_SEQ_MASK, 0x0000000f },
+ { 0x809c, 0x00000000 },
+ { 0x80a0, 0x00000000 },
+ { AR5K_MIC_QOS_CTL, 0x00000000 },
+ { AR5K_MIC_QOS_SEL, 0x00000000 },
+ { AR5K_MISC_MODE, 0x00000000 },
+ { AR5K_OFDM_FIL_CNT, 0x00000000 },
+ { AR5K_CCK_FIL_CNT, 0x00000000 },
+ { AR5K_PHYERR_CNT1, 0x00000000 },
+ { AR5K_PHYERR_CNT1_MASK, 0x00000000 },
+ { AR5K_PHYERR_CNT2, 0x00000000 },
+ { AR5K_PHYERR_CNT2_MASK, 0x00000000 },
+ { AR5K_TSF_THRES, 0x00000000 },
+ { 0x8140, 0x800003f9 },
+ { 0x8144, 0x00000000 },
+ { AR5K_PHY_AGC, 0x00000000 },
+ { AR5K_PHY_ADC_CTL, 0x0000a000 },
+ { 0x983c, 0x00200400 },
+ { AR5K_PHY_GAIN_OFFSET, 0x1284233c },
+ { AR5K_PHY_SCR, 0x0000001f },
+ { AR5K_PHY_SLMT, 0x00000080 },
+ { AR5K_PHY_SCAL, 0x0000000e },
+ { 0x9958, 0x00081fff },
+ { AR5K_PHY_TIMING_7, 0x00000000 },
+ { AR5K_PHY_TIMING_8, 0x02800000 },
+ { AR5K_PHY_TIMING_11, 0x00000000 },
+ { 0x99dc, 0xfebadbe8 },
+ { AR5K_PHY_HEAVY_CLIP_ENABLE, 0x00000000 },
+ { 0x99e4, 0xaaaaaaaa },
+ { 0x99e8, 0x3c466478 },
+ { 0x99ec, 0x000000aa },
+ { AR5K_PHY_SCLOCK, 0x0000000c },
+ { AR5K_PHY_SDELAY, 0x000000ff },
+ { AR5K_PHY_SPENDING, 0x00000014 },
+ { AR5K_PHY_DAG_CCK_CTL, 0x000009b5 },
+ { AR5K_PHY_TXPOWER_RATE3, 0x20202020 },
+ { AR5K_PHY_TXPOWER_RATE4, 0x20202020 },
+ { 0xa23c, 0x93c889af },
+ { AR5K_PHY_FAST_ADC, 0x00000001 },
+ { 0xa250, 0x0000a000 },
+ { AR5K_PHY_BLUETOOTH, 0x00000000 },
+ { AR5K_PHY_TPC_RG1, 0x0cc75380 },
+ { 0xa25c, 0x0f0f0f01 },
+ { 0xa260, 0x5f690f01 },
+ { 0xa264, 0x00418a11 },
+ { 0xa268, 0x00000000 },
+ { AR5K_PHY_TPC_RG5, 0x0c30c166 },
+ { 0xa270, 0x00820820 },
+ { 0xa274, 0x081a3caa },
+ { 0xa278, 0x1ce739ce },
+ { 0xa27c, 0x051701ce },
+ { 0xa300, 0x16010000 },
+ { 0xa304, 0x2c032402 },
+ { 0xa308, 0x48433e42 },
+ { 0xa30c, 0x5a0f500b },
+ { 0xa310, 0x6c4b624a },
+ { 0xa314, 0x7e8b748a },
+ { 0xa318, 0x96cf8ccb },
+ { 0xa31c, 0xa34f9d0f },
+ { 0xa320, 0xa7cfa58f },
+ { 0xa348, 0x3fffffff },
+ { 0xa34c, 0x3fffffff },
+ { 0xa350, 0x3fffffff },
+ { 0xa354, 0x0003ffff },
+ { 0xa358, 0x79a8aa1f },
+ { 0xa35c, 0x066c420f },
+ { 0xa360, 0x0f282207 },
+ { 0xa364, 0x17601685 },
+ { 0xa368, 0x1f801104 },
+ { 0xa36c, 0x37a00c03 },
+ { 0xa370, 0x3fc40883 },
+ { 0xa374, 0x57c00803 },
+ { 0xa378, 0x5fd80682 },
+ { 0xa37c, 0x7fe00482 },
+ { 0xa380, 0x7f3c7bba },
+ { 0xa384, 0xf3307ff0 },
+};
+
+/*
+ * Initial BaseBand Gain settings for RF5111/5112 (AR5210 comes with
+ * RF5110 only so initial BB Gain settings are included in AR5K_AR5210_INI)
+ */
+
+/* RF5111 Initial BaseBand Gain settings */
+static const struct ath5k_ini rf5111_ini_bbgain[] = {
+ { AR5K_BB_GAIN(0), 0x00000000 },
+ { AR5K_BB_GAIN(1), 0x00000020 },
+ { AR5K_BB_GAIN(2), 0x00000010 },
+ { AR5K_BB_GAIN(3), 0x00000030 },
+ { AR5K_BB_GAIN(4), 0x00000008 },
+ { AR5K_BB_GAIN(5), 0x00000028 },
+ { AR5K_BB_GAIN(6), 0x00000004 },
+ { AR5K_BB_GAIN(7), 0x00000024 },
+ { AR5K_BB_GAIN(8), 0x00000014 },
+ { AR5K_BB_GAIN(9), 0x00000034 },
+ { AR5K_BB_GAIN(10), 0x0000000c },
+ { AR5K_BB_GAIN(11), 0x0000002c },
+ { AR5K_BB_GAIN(12), 0x00000002 },
+ { AR5K_BB_GAIN(13), 0x00000022 },
+ { AR5K_BB_GAIN(14), 0x00000012 },
+ { AR5K_BB_GAIN(15), 0x00000032 },
+ { AR5K_BB_GAIN(16), 0x0000000a },
+ { AR5K_BB_GAIN(17), 0x0000002a },
+ { AR5K_BB_GAIN(18), 0x00000006 },
+ { AR5K_BB_GAIN(19), 0x00000026 },
+ { AR5K_BB_GAIN(20), 0x00000016 },
+ { AR5K_BB_GAIN(21), 0x00000036 },
+ { AR5K_BB_GAIN(22), 0x0000000e },
+ { AR5K_BB_GAIN(23), 0x0000002e },
+ { AR5K_BB_GAIN(24), 0x00000001 },
+ { AR5K_BB_GAIN(25), 0x00000021 },
+ { AR5K_BB_GAIN(26), 0x00000011 },
+ { AR5K_BB_GAIN(27), 0x00000031 },
+ { AR5K_BB_GAIN(28), 0x00000009 },
+ { AR5K_BB_GAIN(29), 0x00000029 },
+ { AR5K_BB_GAIN(30), 0x00000005 },
+ { AR5K_BB_GAIN(31), 0x00000025 },
+ { AR5K_BB_GAIN(32), 0x00000015 },
+ { AR5K_BB_GAIN(33), 0x00000035 },
+ { AR5K_BB_GAIN(34), 0x0000000d },
+ { AR5K_BB_GAIN(35), 0x0000002d },
+ { AR5K_BB_GAIN(36), 0x00000003 },
+ { AR5K_BB_GAIN(37), 0x00000023 },
+ { AR5K_BB_GAIN(38), 0x00000013 },
+ { AR5K_BB_GAIN(39), 0x00000033 },
+ { AR5K_BB_GAIN(40), 0x0000000b },
+ { AR5K_BB_GAIN(41), 0x0000002b },
+ { AR5K_BB_GAIN(42), 0x0000002b },
+ { AR5K_BB_GAIN(43), 0x0000002b },
+ { AR5K_BB_GAIN(44), 0x0000002b },
+ { AR5K_BB_GAIN(45), 0x0000002b },
+ { AR5K_BB_GAIN(46), 0x0000002b },
+ { AR5K_BB_GAIN(47), 0x0000002b },
+ { AR5K_BB_GAIN(48), 0x0000002b },
+ { AR5K_BB_GAIN(49), 0x0000002b },
+ { AR5K_BB_GAIN(50), 0x0000002b },
+ { AR5K_BB_GAIN(51), 0x0000002b },
+ { AR5K_BB_GAIN(52), 0x0000002b },
+ { AR5K_BB_GAIN(53), 0x0000002b },
+ { AR5K_BB_GAIN(54), 0x0000002b },
+ { AR5K_BB_GAIN(55), 0x0000002b },
+ { AR5K_BB_GAIN(56), 0x0000002b },
+ { AR5K_BB_GAIN(57), 0x0000002b },
+ { AR5K_BB_GAIN(58), 0x0000002b },
+ { AR5K_BB_GAIN(59), 0x0000002b },
+ { AR5K_BB_GAIN(60), 0x0000002b },
+ { AR5K_BB_GAIN(61), 0x0000002b },
+ { AR5K_BB_GAIN(62), 0x00000002 },
+ { AR5K_BB_GAIN(63), 0x00000016 },
+};
+
+/* RF5112 Initial BaseBand Gain settings (Same for RF5413/5414+) */
+static const struct ath5k_ini rf5112_ini_bbgain[] = {
+ { AR5K_BB_GAIN(0), 0x00000000 },
+ { AR5K_BB_GAIN(1), 0x00000001 },
+ { AR5K_BB_GAIN(2), 0x00000002 },
+ { AR5K_BB_GAIN(3), 0x00000003 },
+ { AR5K_BB_GAIN(4), 0x00000004 },
+ { AR5K_BB_GAIN(5), 0x00000005 },
+ { AR5K_BB_GAIN(6), 0x00000008 },
+ { AR5K_BB_GAIN(7), 0x00000009 },
+ { AR5K_BB_GAIN(8), 0x0000000a },
+ { AR5K_BB_GAIN(9), 0x0000000b },
+ { AR5K_BB_GAIN(10), 0x0000000c },
+ { AR5K_BB_GAIN(11), 0x0000000d },
+ { AR5K_BB_GAIN(12), 0x00000010 },
+ { AR5K_BB_GAIN(13), 0x00000011 },
+ { AR5K_BB_GAIN(14), 0x00000012 },
+ { AR5K_BB_GAIN(15), 0x00000013 },
+ { AR5K_BB_GAIN(16), 0x00000014 },
+ { AR5K_BB_GAIN(17), 0x00000015 },
+ { AR5K_BB_GAIN(18), 0x00000018 },
+ { AR5K_BB_GAIN(19), 0x00000019 },
+ { AR5K_BB_GAIN(20), 0x0000001a },
+ { AR5K_BB_GAIN(21), 0x0000001b },
+ { AR5K_BB_GAIN(22), 0x0000001c },
+ { AR5K_BB_GAIN(23), 0x0000001d },
+ { AR5K_BB_GAIN(24), 0x00000020 },
+ { AR5K_BB_GAIN(25), 0x00000021 },
+ { AR5K_BB_GAIN(26), 0x00000022 },
+ { AR5K_BB_GAIN(27), 0x00000023 },
+ { AR5K_BB_GAIN(28), 0x00000024 },
+ { AR5K_BB_GAIN(29), 0x00000025 },
+ { AR5K_BB_GAIN(30), 0x00000028 },
+ { AR5K_BB_GAIN(31), 0x00000029 },
+ { AR5K_BB_GAIN(32), 0x0000002a },
+ { AR5K_BB_GAIN(33), 0x0000002b },
+ { AR5K_BB_GAIN(34), 0x0000002c },
+ { AR5K_BB_GAIN(35), 0x0000002d },
+ { AR5K_BB_GAIN(36), 0x00000030 },
+ { AR5K_BB_GAIN(37), 0x00000031 },
+ { AR5K_BB_GAIN(38), 0x00000032 },
+ { AR5K_BB_GAIN(39), 0x00000033 },
+ { AR5K_BB_GAIN(40), 0x00000034 },
+ { AR5K_BB_GAIN(41), 0x00000035 },
+ { AR5K_BB_GAIN(42), 0x00000035 },
+ { AR5K_BB_GAIN(43), 0x00000035 },
+ { AR5K_BB_GAIN(44), 0x00000035 },
+ { AR5K_BB_GAIN(45), 0x00000035 },
+ { AR5K_BB_GAIN(46), 0x00000035 },
+ { AR5K_BB_GAIN(47), 0x00000035 },
+ { AR5K_BB_GAIN(48), 0x00000035 },
+ { AR5K_BB_GAIN(49), 0x00000035 },
+ { AR5K_BB_GAIN(50), 0x00000035 },
+ { AR5K_BB_GAIN(51), 0x00000035 },
+ { AR5K_BB_GAIN(52), 0x00000035 },
+ { AR5K_BB_GAIN(53), 0x00000035 },
+ { AR5K_BB_GAIN(54), 0x00000035 },
+ { AR5K_BB_GAIN(55), 0x00000035 },
+ { AR5K_BB_GAIN(56), 0x00000035 },
+ { AR5K_BB_GAIN(57), 0x00000035 },
+ { AR5K_BB_GAIN(58), 0x00000035 },
+ { AR5K_BB_GAIN(59), 0x00000035 },
+ { AR5K_BB_GAIN(60), 0x00000035 },
+ { AR5K_BB_GAIN(61), 0x00000035 },
+ { AR5K_BB_GAIN(62), 0x00000010 },
+ { AR5K_BB_GAIN(63), 0x0000001a },
+};
+
+
+/*
+ * Write initial register dump
+ */
+static void ath5k_hw_ini_registers(struct ath5k_hw *ah, unsigned int size,
+ const struct ath5k_ini *ini_regs, bool skip_pcu)
+{
+ unsigned int i;
+
+ /* Write initial registers */
+ for (i = 0; i < size; i++) {
+ /* Skip PCU registers if
+ * requested */
+ if (skip_pcu &&
+ ini_regs[i].ini_register >= AR5K_PCU_MIN &&
+ ini_regs[i].ini_register <= AR5K_PCU_MAX)
+ continue;
+
+ switch (ini_regs[i].ini_mode) {
+ case AR5K_INI_READ:
+ /* Cleared on read */
+ ath5k_hw_reg_read(ah, ini_regs[i].ini_register);
+ break;
+ case AR5K_INI_WRITE:
+ default:
+ AR5K_REG_WAIT(i);
+ ath5k_hw_reg_write(ah, ini_regs[i].ini_value,
+ ini_regs[i].ini_register);
+ }
+ }
+}
+
+static void ath5k_hw_ini_mode_registers(struct ath5k_hw *ah,
+ unsigned int size, const struct ath5k_ini_mode *ini_mode,
+ u8 mode)
+{
+ unsigned int i;
+
+ for (i = 0; i < size; i++) {
+ AR5K_REG_WAIT(i);
+ ath5k_hw_reg_write(ah, ini_mode[i].mode_value[mode],
+ (u32)ini_mode[i].mode_register);
+ }
+
+}
+
+int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool skip_pcu)
+{
+ /*
+ * Write initial register settings
+ */
+
+ /* For AR5212 and compatible */
+ if (ah->ah_version == AR5K_AR5212) {
+
+ /* First set of mode-specific settings */
+ ath5k_hw_ini_mode_registers(ah,
+ ARRAY_SIZE(ar5212_ini_mode_start),
+ ar5212_ini_mode_start, mode);
+
+ /*
+ * Write initial settings common for all modes
+ */
+ ath5k_hw_ini_registers(ah, ARRAY_SIZE(ar5212_ini_common_start),
+ ar5212_ini_common_start, skip_pcu);
+
+ /* Second set of mode-specific settings */
+ switch (ah->ah_radio) {
+ case AR5K_RF5111:
+
+ ath5k_hw_ini_mode_registers(ah,
+ ARRAY_SIZE(rf5111_ini_mode_end),
+ rf5111_ini_mode_end, mode);
+
+ ath5k_hw_ini_registers(ah,
+ ARRAY_SIZE(rf5111_ini_common_end),
+ rf5111_ini_common_end, skip_pcu);
+
+ /* Baseband gain table */
+ ath5k_hw_ini_registers(ah,
+ ARRAY_SIZE(rf5111_ini_bbgain),
+ rf5111_ini_bbgain, skip_pcu);
+
+ break;
+ case AR5K_RF5112:
+
+ ath5k_hw_ini_mode_registers(ah,
+ ARRAY_SIZE(rf5112_ini_mode_end),
+ rf5112_ini_mode_end, mode);
+
+ ath5k_hw_ini_registers(ah,
+ ARRAY_SIZE(rf5112_ini_common_end),
+ rf5112_ini_common_end, skip_pcu);
+
+ ath5k_hw_ini_registers(ah,
+ ARRAY_SIZE(rf5112_ini_bbgain),
+ rf5112_ini_bbgain, skip_pcu);
+
+ break;
+ case AR5K_RF5413:
+
+ ath5k_hw_ini_mode_registers(ah,
+ ARRAY_SIZE(rf5413_ini_mode_end),
+ rf5413_ini_mode_end, mode);
+
+ ath5k_hw_ini_registers(ah,
+ ARRAY_SIZE(rf5413_ini_common_end),
+ rf5413_ini_common_end, skip_pcu);
+
+ ath5k_hw_ini_registers(ah,
+ ARRAY_SIZE(rf5112_ini_bbgain),
+ rf5112_ini_bbgain, skip_pcu);
+
+ break;
+ case AR5K_RF2316:
+ case AR5K_RF2413:
+
+ ath5k_hw_ini_mode_registers(ah,
+ ARRAY_SIZE(rf2413_ini_mode_end),
+ rf2413_ini_mode_end, mode);
+
+ ath5k_hw_ini_registers(ah,
+ ARRAY_SIZE(rf2413_ini_common_end),
+ rf2413_ini_common_end, skip_pcu);
+
+ /* Override settings from rf2413_ini_common_end */
+ if (ah->ah_radio == AR5K_RF2316) {
+ ath5k_hw_reg_write(ah, 0x00004000,
+ AR5K_PHY_AGC);
+ ath5k_hw_reg_write(ah, 0x081b7caa,
+ 0xa274);
+ }
+
+ ath5k_hw_ini_registers(ah,
+ ARRAY_SIZE(rf5112_ini_bbgain),
+ rf5112_ini_bbgain, skip_pcu);
+ break;
+ case AR5K_RF2317:
+
+ ath5k_hw_ini_mode_registers(ah,
+ ARRAY_SIZE(rf2413_ini_mode_end),
+ rf2413_ini_mode_end, mode);
+
+ ath5k_hw_ini_registers(ah,
+ ARRAY_SIZE(rf2425_ini_common_end),
+ rf2425_ini_common_end, skip_pcu);
+
+ /* Override settings from rf2413_ini_mode_end */
+ ath5k_hw_reg_write(ah, 0x00180a65, AR5K_PHY_GAIN);
+
+ /* Override settings from rf2413_ini_common_end */
+ ath5k_hw_reg_write(ah, 0x00004000, AR5K_PHY_AGC);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TPC_RG5,
+ AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP, 0xa);
+ ath5k_hw_reg_write(ah, 0x800000a8, 0x8140);
+ ath5k_hw_reg_write(ah, 0x000000ff, 0x9958);
+
+ ath5k_hw_ini_registers(ah,
+ ARRAY_SIZE(rf5112_ini_bbgain),
+ rf5112_ini_bbgain, skip_pcu);
+ break;
+ case AR5K_RF2425:
+
+ ath5k_hw_ini_mode_registers(ah,
+ ARRAY_SIZE(rf2425_ini_mode_end),
+ rf2425_ini_mode_end, mode);
+
+ ath5k_hw_ini_registers(ah,
+ ARRAY_SIZE(rf2425_ini_common_end),
+ rf2425_ini_common_end, skip_pcu);
+
+ ath5k_hw_ini_registers(ah,
+ ARRAY_SIZE(rf5112_ini_bbgain),
+ rf5112_ini_bbgain, skip_pcu);
+ break;
+ default:
+ return -EINVAL;
+
+ }
+
+ /* For AR5211 */
+ } else if (ah->ah_version == AR5K_AR5211) {
+
+ /* AR5K_MODE_11B */
+ if (mode > 2) {
+ ATH5K_ERR(ah,
+ "unsupported channel mode: %d\n", mode);
+ return -EINVAL;
+ }
+
+ /* Mode-specific settings */
+ ath5k_hw_ini_mode_registers(ah, ARRAY_SIZE(ar5211_ini_mode),
+ ar5211_ini_mode, mode);
+
+ /*
+ * Write initial settings common for all modes
+ */
+ ath5k_hw_ini_registers(ah, ARRAY_SIZE(ar5211_ini),
+ ar5211_ini, skip_pcu);
+
+ /* AR5211 only comes with 5111 */
+
+ /* Baseband gain table */
+ ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf5111_ini_bbgain),
+ rf5111_ini_bbgain, skip_pcu);
+ /* For AR5210 (for mode settings check out ath5k_hw_reset_tx_queue) */
+ } else if (ah->ah_version == AR5K_AR5210) {
+ ath5k_hw_ini_registers(ah, ARRAY_SIZE(ar5210_ini),
+ ar5210_ini, skip_pcu);
+ }
+
+ return 0;
+}
diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c
new file mode 100644
index 0000000..c1151c7
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/led.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2005 Atheros Communications, Inc.
+ * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
+ * Copyright (c) 2009 Bob Copeland <me@bobcopeland.com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * 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 NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ */
+
+#include <linux/pci.h>
+#include "ath5k.h"
+
+#define ATH_SDEVICE(subv, subd) \
+ .vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \
+ .subvendor = (subv), .subdevice = (subd)
+
+#define ATH_LED(pin, polarity) .driver_data = (((pin) << 8) | (polarity))
+#define ATH_PIN(data) ((data) >> 8)
+#define ATH_POLARITY(data) ((data) & 0xff)
+
+/* Devices we match on for LED config info (typically laptops) */
+static DEFINE_PCI_DEVICE_TABLE(ath5k_led_devices) = {
+ /* AR5211 */
+ { PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5211), ATH_LED(0, 0) },
+ /* HP Compaq nc6xx, nc4000, nx6000 */
+ { ATH_SDEVICE(PCI_VENDOR_ID_COMPAQ, PCI_ANY_ID), ATH_LED(1, 1) },
+ /* Acer Aspire One A150 (maximlevitsky@gmail.com) */
+ { ATH_SDEVICE(PCI_VENDOR_ID_FOXCONN, 0xe008), ATH_LED(3, 0) },
+ /* Acer Aspire One AO531h AO751h (keng-yu.lin@canonical.com) */
+ { ATH_SDEVICE(PCI_VENDOR_ID_FOXCONN, 0xe00d), ATH_LED(3, 0) },
+ /* Acer Ferrari 5000 (russ.dill@gmail.com) */
+ { ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0422), ATH_LED(1, 1) },
+ /* E-machines E510 (tuliom@gmail.com) */
+ { ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0428), ATH_LED(3, 0) },
+ /* BenQ Joybook R55v (nowymarluk@wp.pl) */
+ { ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0100), ATH_LED(1, 0) },
+ /* Acer Extensa 5620z (nekoreeve@gmail.com) */
+ { ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0105), ATH_LED(3, 0) },
+ /* Fukato Datacask Jupiter 1014a (mrb74@gmx.at) */
+ { ATH_SDEVICE(PCI_VENDOR_ID_AZWAVE, 0x1026), ATH_LED(3, 0) },
+ /* IBM ThinkPad AR5BXB6 (legovini@spiro.fisica.unipd.it) */
+ { ATH_SDEVICE(PCI_VENDOR_ID_IBM, 0x058a), ATH_LED(1, 0) },
+ /* HP Compaq CQ60-206US (ddreggors@jumptv.com) */
+ { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137a), ATH_LED(3, 1) },
+ /* HP Compaq C700 (nitrousnrg@gmail.com) */
+ { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 1) },
+ /* LiteOn AR5BXB63 (magooz@salug.it) */
+ { ATH_SDEVICE(PCI_VENDOR_ID_ATHEROS, 0x3067), ATH_LED(3, 0) },
+ /* IBM-specific AR5212 (all others) */
+ { PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5212_IBM), ATH_LED(0, 0) },
+ /* Dell Vostro A860 (shahar@shahar-or.co.il) */
+ { ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0112), ATH_LED(3, 0) },
+ { }
+};
+
+void ath5k_led_enable(struct ath5k_hw *ah)
+{
+ if (test_bit(ATH_STAT_LEDSOFT, ah->status)) {
+ ath5k_hw_set_gpio_output(ah, ah->led_pin);
+ ath5k_led_off(ah);
+ }
+}
+
+static void ath5k_led_on(struct ath5k_hw *ah)
+{
+ if (!test_bit(ATH_STAT_LEDSOFT, ah->status))
+ return;
+ ath5k_hw_set_gpio(ah, ah->led_pin, ah->led_on);
+}
+
+void ath5k_led_off(struct ath5k_hw *ah)
+{
+ if (!test_bit(ATH_STAT_LEDSOFT, ah->status))
+ return;
+ ath5k_hw_set_gpio(ah, ah->led_pin, !ah->led_on);
+}
+
+static void
+ath5k_led_brightness_set(struct led_classdev *led_dev,
+ enum led_brightness brightness)
+{
+ struct ath5k_led *led = container_of(led_dev, struct ath5k_led,
+ led_dev);
+
+ if (brightness == LED_OFF)
+ ath5k_led_off(led->ah);
+ else
+ ath5k_led_on(led->ah);
+}
+
+static int
+ath5k_register_led(struct ath5k_hw *ah, struct ath5k_led *led,
+ const char *name, char *trigger)
+{
+ int err;
+
+ led->ah = ah;
+ strncpy(led->name, name, sizeof(led->name));
+ led->led_dev.name = led->name;
+ led->led_dev.default_trigger = trigger;
+ led->led_dev.brightness_set = ath5k_led_brightness_set;
+
+ err = led_classdev_register(ah->dev, &led->led_dev);
+ if (err) {
+ ATH5K_WARN(ah, "could not register LED %s\n", name);
+ led->ah = NULL;
+ }
+ return err;
+}
+
+static void
+ath5k_unregister_led(struct ath5k_led *led)
+{
+ if (!led->ah)
+ return;
+ led_classdev_unregister(&led->led_dev);
+ ath5k_led_off(led->ah);
+ led->ah = NULL;
+}
+
+void ath5k_unregister_leds(struct ath5k_hw *ah)
+{
+ ath5k_unregister_led(&ah->rx_led);
+ ath5k_unregister_led(&ah->tx_led);
+}
+
+int __devinit ath5k_init_leds(struct ath5k_hw *ah)
+{
+ int ret = 0;
+ struct ieee80211_hw *hw = ah->hw;
+#ifndef CONFIG_ATHEROS_AR231X
+ struct pci_dev *pdev = ah->pdev;
+#endif
+ char name[ATH5K_LED_MAX_NAME_LEN + 1];
+ const struct pci_device_id *match;
+
+ if (!ah->pdev)
+ return 0;
+
+#ifdef CONFIG_ATHEROS_AR231X
+ match = NULL;
+#else
+ match = pci_match_id(&ath5k_led_devices[0], pdev);
+#endif
+ if (match) {
+ __set_bit(ATH_STAT_LEDSOFT, ah->status);
+ ah->led_pin = ATH_PIN(match->driver_data);
+ ah->led_on = ATH_POLARITY(match->driver_data);
+ }
+
+ if (!test_bit(ATH_STAT_LEDSOFT, ah->status))
+ goto out;
+
+ ath5k_led_enable(ah);
+
+ snprintf(name, sizeof(name), "ath5k-%s::rx", wiphy_name(hw->wiphy));
+ ret = ath5k_register_led(ah, &ah->rx_led, name,
+ ieee80211_get_rx_led_name(hw));
+ if (ret)
+ goto out;
+
+ snprintf(name, sizeof(name), "ath5k-%s::tx", wiphy_name(hw->wiphy));
+ ret = ath5k_register_led(ah, &ah->tx_led, name,
+ ieee80211_get_tx_led_name(hw));
+out:
+ return ret;
+}
+
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
new file mode 100644
index 0000000..2253664
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -0,0 +1,836 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2005 Atheros Communications, Inc.
+ * Copyright (c) 2006 Devicescape Software, Inc.
+ * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
+ * Copyright (c) 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ * Copyright (c) 2010 Bruno Randolf <br1@einfach.org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * 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 NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ */
+
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+
+#include "ath5k.h"
+#include "base.h"
+#include "reg.h"
+
+/********************\
+* Mac80211 functions *
+\********************/
+
+static void
+ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ struct ath5k_hw *ah = hw->priv;
+ u16 qnum = skb_get_queue_mapping(skb);
+
+ if (WARN_ON(qnum >= ah->ah_capabilities.cap_queues.q_tx_num)) {
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
+ ath5k_tx_queue(hw, skb, &ah->txqs[qnum]);
+}
+
+
+static int
+ath5k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct ath5k_hw *ah = hw->priv;
+ int ret;
+ struct ath5k_vif *avf = (void *)vif->drv_priv;
+
+ mutex_lock(&ah->lock);
+
+ if ((vif->type == NL80211_IFTYPE_AP ||
+ vif->type == NL80211_IFTYPE_ADHOC)
+ && (ah->num_ap_vifs + ah->num_adhoc_vifs) >= ATH_BCBUF) {
+ ret = -ELNRNG;
+ goto end;
+ }
+
+ /* Don't allow other interfaces if one ad-hoc is configured.
+ * TODO: Fix the problems with ad-hoc and multiple other interfaces.
+ * We would need to operate the HW in ad-hoc mode to allow TSF updates
+ * for the IBSS, but this breaks with additional AP or STA interfaces
+ * at the moment. */
+ if (ah->num_adhoc_vifs ||
+ (ah->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
+ ATH5K_ERR(ah, "Only one single ad-hoc interface is allowed.\n");
+ ret = -ELNRNG;
+ goto end;
+ }
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ avf->opmode = vif->type;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ goto end;
+ }
+
+ ah->nvifs++;
+ ATH5K_DBG(ah, ATH5K_DEBUG_MODE, "add interface mode %d\n", avf->opmode);
+
+ /* Assign the vap/adhoc to a beacon xmit slot. */
+ if ((avf->opmode == NL80211_IFTYPE_AP) ||
+ (avf->opmode == NL80211_IFTYPE_ADHOC) ||
+ (avf->opmode == NL80211_IFTYPE_MESH_POINT)) {
+ int slot;
+
+ WARN_ON(list_empty(&ah->bcbuf));
+ avf->bbuf = list_first_entry(&ah->bcbuf, struct ath5k_buf,
+ list);
+ list_del(&avf->bbuf->list);
+
+ avf->bslot = 0;
+ for (slot = 0; slot < ATH_BCBUF; slot++) {
+ if (!ah->bslot[slot]) {
+ avf->bslot = slot;
+ break;
+ }
+ }
+ BUG_ON(ah->bslot[avf->bslot] != NULL);
+ ah->bslot[avf->bslot] = vif;
+ if (avf->opmode == NL80211_IFTYPE_AP)
+ ah->num_ap_vifs++;
+ else if (avf->opmode == NL80211_IFTYPE_ADHOC)
+ ah->num_adhoc_vifs++;
+ }
+
+ /* Any MAC address is fine, all others are included through the
+ * filter.
+ */
+ ath5k_hw_set_lladdr(ah, vif->addr);
+
+ ath5k_update_bssid_mask_and_opmode(ah, vif);
+ ret = 0;
+end:
+ mutex_unlock(&ah->lock);
+ return ret;
+}
+
+
+static void
+ath5k_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct ath5k_hw *ah = hw->priv;
+ struct ath5k_vif *avf = (void *)vif->drv_priv;
+ unsigned int i;
+
+ mutex_lock(&ah->lock);
+ ah->nvifs--;
+
+ if (avf->bbuf) {
+ ath5k_txbuf_free_skb(ah, avf->bbuf);
+ list_add_tail(&avf->bbuf->list, &ah->bcbuf);
+ for (i = 0; i < ATH_BCBUF; i++) {
+ if (ah->bslot[i] == vif) {
+ ah->bslot[i] = NULL;
+ break;
+ }
+ }
+ avf->bbuf = NULL;
+ }
+ if (avf->opmode == NL80211_IFTYPE_AP)
+ ah->num_ap_vifs--;
+ else if (avf->opmode == NL80211_IFTYPE_ADHOC)
+ ah->num_adhoc_vifs--;
+
+ ath5k_update_bssid_mask_and_opmode(ah, NULL);
+ mutex_unlock(&ah->lock);
+}
+
+
+/*
+ * TODO: Phy disable/diversity etc
+ */
+static int
+ath5k_config(struct ieee80211_hw *hw, u32 changed)
+{
+ struct ath5k_hw *ah = hw->priv;
+ struct ieee80211_conf *conf = &hw->conf;
+ int ret = 0;
+ int i;
+
+ mutex_lock(&ah->lock);
+
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+ ret = ath5k_chan_set(ah, conf->channel);
+ if (ret < 0)
+ goto unlock;
+ }
+
+ if ((changed & IEEE80211_CONF_CHANGE_POWER) &&
+ (ah->power_level != conf->power_level)) {
+ ah->power_level = conf->power_level;
+
+ /* Half dB steps */
+ ath5k_hw_set_txpower_limit(ah, (conf->power_level * 2));
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
+ ah->ah_retry_long = conf->long_frame_max_tx_count;
+ ah->ah_retry_short = conf->short_frame_max_tx_count;
+
+ for (i = 0; i < ah->ah_capabilities.cap_queues.q_tx_num; i++)
+ ath5k_hw_set_tx_retry_limits(ah, i);
+ }
+
+ /* TODO:
+ * 1) Move this on config_interface and handle each case
+ * separately eg. when we have only one STA vif, use
+ * AR5K_ANTMODE_SINGLE_AP
+ *
+ * 2) Allow the user to change antenna mode eg. when only
+ * one antenna is present
+ *
+ * 3) Allow the user to set default/tx antenna when possible
+ *
+ * 4) Default mode should handle 90% of the cases, together
+ * with fixed a/b and single AP modes we should be able to
+ * handle 99%. Sectored modes are extreme cases and i still
+ * haven't found a usage for them. If we decide to support them,
+ * then we must allow the user to set how many tx antennas we
+ * have available
+ */
+ ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode);
+
+unlock:
+ mutex_unlock(&ah->lock);
+ return ret;
+}
+
+
+static void
+ath5k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf, u32 changes)
+{
+ struct ath5k_vif *avf = (void *)vif->drv_priv;
+ struct ath5k_hw *ah = hw->priv;
+ struct ath_common *common = ath5k_hw_common(ah);
+ unsigned long flags;
+
+ mutex_lock(&ah->lock);
+
+ if (changes & BSS_CHANGED_BSSID) {
+ /* Cache for later use during resets */
+ memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
+ common->curaid = 0;
+ ath5k_hw_set_bssid(ah);
+ mmiowb();
+ }
+
+ if (changes & BSS_CHANGED_BEACON_INT)
+ ah->bintval = bss_conf->beacon_int;
+
+ if (changes & BSS_CHANGED_ERP_SLOT) {
+ int slot_time;
+
+ ah->ah_short_slot = bss_conf->use_short_slot;
+ slot_time = ath5k_hw_get_default_slottime(ah) +
+ 3 * ah->ah_coverage_class;
+ ath5k_hw_set_ifs_intervals(ah, slot_time);
+ }
+
+ if (changes & BSS_CHANGED_ASSOC) {
+ avf->assoc = bss_conf->assoc;
+ if (bss_conf->assoc)
+ ah->assoc = bss_conf->assoc;
+ else
+ ah->assoc = ath5k_any_vif_assoc(ah);
+
+ if (ah->opmode == NL80211_IFTYPE_STATION)
+ ath5k_set_beacon_filter(hw, ah->assoc);
+ ath5k_hw_set_ledstate(ah, ah->assoc ?
+ AR5K_LED_ASSOC : AR5K_LED_INIT);
+ if (bss_conf->assoc) {
+ ATH5K_DBG(ah, ATH5K_DEBUG_ANY,
+ "Bss Info ASSOC %d, bssid: %pM\n",
+ bss_conf->aid, common->curbssid);
+ common->curaid = bss_conf->aid;
+ ath5k_hw_set_bssid(ah);
+ /* Once ANI is available you would start it here */
+ }
+ }
+
+ if (changes & BSS_CHANGED_BEACON) {
+ spin_lock_irqsave(&ah->block, flags);
+ ath5k_beacon_update(hw, vif);
+ spin_unlock_irqrestore(&ah->block, flags);
+ }
+
+ if (changes & BSS_CHANGED_BEACON_ENABLED)
+ ah->enable_beacon = bss_conf->enable_beacon;
+
+ if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED |
+ BSS_CHANGED_BEACON_INT))
+ ath5k_beacon_config(ah);
+
+ mutex_unlock(&ah->lock);
+}
+
+
+static u64
+ath5k_prepare_multicast(struct ieee80211_hw *hw,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ struct netdev_hw_addr_list *mc_list)
+#else
+ int mc_count, struct dev_addr_list *ha)
+#endif
+{
+ u32 mfilt[2], val;
+ u8 pos;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ struct netdev_hw_addr *ha;
+#else
+ int i;
+#endif
+
+ mfilt[0] = 0;
+ mfilt[1] = 1;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ netdev_hw_addr_list_for_each(ha, mc_list) {
+#else
+ for (i = 0; i < mc_count; i++) {
+ if (!ha)
+ break;
+#endif
+ /* calculate XOR of eight 6-bit values */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ val = get_unaligned_le32(ha->addr + 0);
+#else
+ val = get_unaligned_le32(ha->dmi_addr + 0);
+#endif
+ pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+ val = get_unaligned_le32(ha->addr + 3);
+#else
+ val = get_unaligned_le32(ha->dmi_addr + 3);
+#endif
+ pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
+ pos &= 0x3f;
+ mfilt[pos / 32] |= (1 << (pos % 32));
+ /* XXX: we might be able to just do this instead,
+ * but not sure, needs testing, if we do use this we'd
+ * need to inform below not to reset the mcast */
+ /* ath5k_hw_set_mcast_filterindex(ah,
+ * ha->addr[5]); */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35))
+ ha = ha->next;
+#endif
+ }
+
+ return ((u64)(mfilt[1]) << 32) | mfilt[0];
+}
+
+
+/*
+ * o always accept unicast, broadcast, and multicast traffic
+ * o multicast traffic for all BSSIDs will be enabled if mac80211
+ * says it should be
+ * o maintain current state of phy ofdm or phy cck error reception.
+ * If the hardware detects any of these type of errors then
+ * ath5k_hw_get_rx_filter() will pass to us the respective
+ * hardware filters to be able to receive these type of frames.
+ * o probe request frames are accepted only when operating in
+ * hostap, adhoc, or monitor modes
+ * o enable promiscuous mode according to the interface state
+ * o accept beacons:
+ * - when operating in adhoc mode so the 802.11 layer creates
+ * node table entries for peers,
+ * - when operating in station mode for collecting rssi data when
+ * the station is otherwise quiet, or
+ * - when scanning
+ */
+static void
+ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
+ unsigned int *new_flags, u64 multicast)
+{
+#define SUPPORTED_FIF_FLAGS \
+ (FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | \
+ FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \
+ FIF_BCN_PRBRESP_PROMISC)
+
+ struct ath5k_hw *ah = hw->priv;
+ u32 mfilt[2], rfilt;
+ struct ath5k_vif_iter_data iter_data; /* to count STA interfaces */
+
+ mutex_lock(&ah->lock);
+
+ mfilt[0] = multicast;
+ mfilt[1] = multicast >> 32;
+
+ /* Only deal with supported flags */
+ changed_flags &= SUPPORTED_FIF_FLAGS;
+ *new_flags &= SUPPORTED_FIF_FLAGS;
+
+ /* If HW detects any phy or radar errors, leave those filters on.
+ * Also, always enable Unicast, Broadcasts and Multicast
+ * XXX: move unicast, bssid broadcasts and multicast to mac80211 */
+ rfilt = (ath5k_hw_get_rx_filter(ah) & (AR5K_RX_FILTER_PHYERR)) |
+ (AR5K_RX_FILTER_UCAST | AR5K_RX_FILTER_BCAST |
+ AR5K_RX_FILTER_MCAST);
+
+ if (changed_flags & (FIF_PROMISC_IN_BSS | FIF_OTHER_BSS)) {
+ if (*new_flags & FIF_PROMISC_IN_BSS)
+ __set_bit(ATH_STAT_PROMISC, ah->status);
+ else
+ __clear_bit(ATH_STAT_PROMISC, ah->status);
+ }
+
+ if (test_bit(ATH_STAT_PROMISC, ah->status))
+ rfilt |= AR5K_RX_FILTER_PROM;
+
+ /* Note, AR5K_RX_FILTER_MCAST is already enabled */
+ if (*new_flags & FIF_ALLMULTI) {
+ mfilt[0] = ~0;
+ mfilt[1] = ~0;
+ }
+
+ /* This is the best we can do */
+ if (*new_flags & (FIF_FCSFAIL | FIF_PLCPFAIL))
+ rfilt |= AR5K_RX_FILTER_PHYERR;
+
+ /* FIF_BCN_PRBRESP_PROMISC really means to enable beacons
+ * and probes for any BSSID */
+ if ((*new_flags & FIF_BCN_PRBRESP_PROMISC) || (ah->nvifs > 1))
+ rfilt |= AR5K_RX_FILTER_BEACON;
+
+ /* FIF_CONTROL doc says that if FIF_PROMISC_IN_BSS is not
+ * set we should only pass on control frames for this
+ * station. This needs testing. I believe right now this
+ * enables *all* control frames, which is OK.. but
+ * but we should see if we can improve on granularity */
+ if (*new_flags & FIF_CONTROL)
+ rfilt |= AR5K_RX_FILTER_CONTROL;
+
+ /* Additional settings per mode -- this is per ath5k */
+
+ /* XXX move these to mac80211, and add a beacon IFF flag to mac80211 */
+
+ switch (ah->opmode) {
+ case NL80211_IFTYPE_MESH_POINT:
+ rfilt |= AR5K_RX_FILTER_CONTROL |
+ AR5K_RX_FILTER_BEACON |
+ AR5K_RX_FILTER_PROBEREQ |
+ AR5K_RX_FILTER_PROM;
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_ADHOC:
+ rfilt |= AR5K_RX_FILTER_PROBEREQ |
+ AR5K_RX_FILTER_BEACON;
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (ah->assoc)
+ rfilt |= AR5K_RX_FILTER_BEACON;
+ default:
+ break;
+ }
+
+ iter_data.hw_macaddr = NULL;
+ iter_data.n_stas = 0;
+ iter_data.need_set_hw_addr = false;
+ ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter,
+ &iter_data);
+
+ /* Set up RX Filter */
+ if (iter_data.n_stas > 1) {
+ /* If you have multiple STA interfaces connected to
+ * different APs, ARPs are not received (most of the time?)
+ * Enabling PROMISC appears to fix that problem.
+ */
+ rfilt |= AR5K_RX_FILTER_PROM;
+ }
+
+ /* Set filters */
+ ath5k_hw_set_rx_filter(ah, rfilt);
+
+ /* Set multicast bits */
+ ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]);
+ /* Set the cached hw filter flags, this will later actually
+ * be set in HW */
+ ah->filter_flags = rfilt;
+
+ mutex_unlock(&ah->lock);
+}
+
+
+static int
+ath5k_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 ath5k_hw *ah = hw->priv;
+ struct ath_common *common = ath5k_hw_common(ah);
+ int ret = 0;
+
+ if (ath5k_modparam_nohwcrypt)
+ return -EOPNOTSUPP;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ case WLAN_CIPHER_SUITE_TKIP:
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (common->crypt_caps & ATH_CRYPT_CAP_CIPHER_AESCCM)
+ break;
+ return -EOPNOTSUPP;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ mutex_lock(&ah->lock);
+
+ switch (cmd) {
+ case SET_KEY:
+ ret = ath_key_config(common, vif, sta, key);
+ if (ret >= 0) {
+ key->hw_key_idx = ret;
+ /* push IV and Michael MIC generation to stack */
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+ if (key->cipher == WLAN_CIPHER_SUITE_CCMP)
+ key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
+ ret = 0;
+ }
+ break;
+ case DISABLE_KEY:
+ ath_key_delete(common, key);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mmiowb();
+ mutex_unlock(&ah->lock);
+ return ret;
+}
+
+
+static void
+ath5k_sw_scan_start(struct ieee80211_hw *hw)
+{
+ struct ath5k_hw *ah = hw->priv;
+ if (!ah->assoc)
+ ath5k_hw_set_ledstate(ah, AR5K_LED_SCAN);
+}
+
+
+static void
+ath5k_sw_scan_complete(struct ieee80211_hw *hw)
+{
+ struct ath5k_hw *ah = hw->priv;
+ ath5k_hw_set_ledstate(ah, ah->assoc ?
+ AR5K_LED_ASSOC : AR5K_LED_INIT);
+}
+
+
+static int
+ath5k_get_stats(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct ath5k_hw *ah = hw->priv;
+
+ /* Force update */
+ ath5k_hw_update_mib_counters(ah);
+
+ stats->dot11ACKFailureCount = ah->stats.ack_fail;
+ stats->dot11RTSFailureCount = ah->stats.rts_fail;
+ stats->dot11RTSSuccessCount = ah->stats.rts_ok;
+ stats->dot11FCSErrorCount = ah->stats.fcs_error;
+
+ return 0;
+}
+
+
+static int
+ath5k_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct ath5k_hw *ah = hw->priv;
+ struct ath5k_txq_info qi;
+ int ret = 0;
+
+ if (queue >= ah->ah_capabilities.cap_queues.q_tx_num)
+ return 0;
+
+ mutex_lock(&ah->lock);
+
+ ath5k_hw_get_tx_queueprops(ah, queue, &qi);
+
+ qi.tqi_aifs = params->aifs;
+ qi.tqi_cw_min = params->cw_min;
+ qi.tqi_cw_max = params->cw_max;
+ qi.tqi_burst_time = params->txop;
+
+ ATH5K_DBG(ah, ATH5K_DEBUG_ANY,
+ "Configure tx [queue %d], "
+ "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
+ queue, params->aifs, params->cw_min,
+ params->cw_max, params->txop);
+
+ if (ath5k_hw_set_tx_queueprops(ah, queue, &qi)) {
+ ATH5K_ERR(ah,
+ "Unable to update hardware queue %u!\n", queue);
+ ret = -EIO;
+ } else
+ ath5k_hw_reset_tx_queue(ah, queue);
+
+ mutex_unlock(&ah->lock);
+
+ return ret;
+}
+
+
+static u64
+ath5k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct ath5k_hw *ah = hw->priv;
+
+ return ath5k_hw_get_tsf64(ah);
+}
+
+
+static void
+ath5k_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u64 tsf)
+{
+ struct ath5k_hw *ah = hw->priv;
+
+ ath5k_hw_set_tsf64(ah, tsf);
+}
+
+
+static void
+ath5k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct ath5k_hw *ah = hw->priv;
+
+ /*
+ * in IBSS mode we need to update the beacon timers too.
+ * this will also reset the TSF if we call it with 0
+ */
+ if (ah->opmode == NL80211_IFTYPE_ADHOC)
+ ath5k_beacon_update_timers(ah, 0);
+ else
+ ath5k_hw_reset_tsf(ah);
+}
+
+
+static int
+ath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey)
+{
+ struct ath5k_hw *ah = hw->priv;
+ struct ieee80211_conf *conf = &hw->conf;
+ struct ath_common *common = ath5k_hw_common(ah);
+ struct ath_cycle_counters *cc = &common->cc_survey;
+ unsigned int div = common->clockrate * 1000;
+
+ if (idx != 0)
+ return -ENOENT;
+
+ spin_lock_bh(&common->cc_lock);
+ ath_hw_cycle_counters_update(common);
+ if (cc->cycles > 0) {
+ ah->survey.channel_time += cc->cycles / div;
+ ah->survey.channel_time_busy += cc->rx_busy / div;
+ ah->survey.channel_time_rx += cc->rx_frame / div;
+ ah->survey.channel_time_tx += cc->tx_frame / div;
+ }
+ memset(cc, 0, sizeof(*cc));
+ spin_unlock_bh(&common->cc_lock);
+
+ memcpy(survey, &ah->survey, sizeof(*survey));
+
+ survey->channel = conf->channel;
+ survey->noise = ah->ah_noise_floor;
+ survey->filled = SURVEY_INFO_NOISE_DBM |
+ SURVEY_INFO_CHANNEL_TIME |
+ SURVEY_INFO_CHANNEL_TIME_BUSY |
+ SURVEY_INFO_CHANNEL_TIME_RX |
+ SURVEY_INFO_CHANNEL_TIME_TX;
+
+ return 0;
+}
+
+
+/**
+ * ath5k_set_coverage_class - Set IEEE 802.11 coverage class
+ *
+ * @hw: struct ieee80211_hw pointer
+ * @coverage_class: IEEE 802.11 coverage class number
+ *
+ * Mac80211 callback. Sets slot time, ACK timeout and CTS timeout for given
+ * coverage class. The values are persistent, they are restored after device
+ * reset.
+ */
+static void
+ath5k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
+{
+ struct ath5k_hw *ah = hw->priv;
+
+ mutex_lock(&ah->lock);
+ ath5k_hw_set_coverage_class(ah, coverage_class);
+ mutex_unlock(&ah->lock);
+}
+
+
+static int
+ath5k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+{
+ struct ath5k_hw *ah = hw->priv;
+
+ if (tx_ant == 1 && rx_ant == 1)
+ ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_A);
+ else if (tx_ant == 2 && rx_ant == 2)
+ ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_B);
+ else if ((tx_ant & 3) == 3 && (rx_ant & 3) == 3)
+ ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT);
+ else
+ return -EINVAL;
+ return 0;
+}
+
+
+static int
+ath5k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
+{
+ struct ath5k_hw *ah = hw->priv;
+
+ switch (ah->ah_ant_mode) {
+ case AR5K_ANTMODE_FIXED_A:
+ *tx_ant = 1; *rx_ant = 1; break;
+ case AR5K_ANTMODE_FIXED_B:
+ *tx_ant = 2; *rx_ant = 2; break;
+ case AR5K_ANTMODE_DEFAULT:
+ *tx_ant = 3; *rx_ant = 3; break;
+ }
+ return 0;
+}
+
+
+static void ath5k_get_ringparam(struct ieee80211_hw *hw,
+ u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max)
+{
+ struct ath5k_hw *ah = hw->priv;
+
+ *tx = ah->txqs[AR5K_TX_QUEUE_ID_DATA_MIN].txq_max;
+
+ *tx_max = ATH5K_TXQ_LEN_MAX;
+ *rx = *rx_max = ATH_RXBUF;
+}
+
+
+static int ath5k_set_ringparam(struct ieee80211_hw *hw, u32 tx, u32 rx)
+{
+ struct ath5k_hw *ah = hw->priv;
+ u16 qnum;
+
+ /* only support setting tx ring size for now */
+ if (rx != ATH_RXBUF)
+ return -EINVAL;
+
+ /* restrict tx ring size min/max */
+ if (!tx || tx > ATH5K_TXQ_LEN_MAX)
+ return -EINVAL;
+
+ for (qnum = 0; qnum < ARRAY_SIZE(ah->txqs); qnum++) {
+ if (!ah->txqs[qnum].setup)
+ continue;
+ if (ah->txqs[qnum].qnum < AR5K_TX_QUEUE_ID_DATA_MIN ||
+ ah->txqs[qnum].qnum > AR5K_TX_QUEUE_ID_DATA_MAX)
+ continue;
+
+ ah->txqs[qnum].txq_max = tx;
+ if (ah->txqs[qnum].txq_len >= ah->txqs[qnum].txq_max)
+ ieee80211_stop_queue(hw, ah->txqs[qnum].qnum);
+ }
+
+ return 0;
+}
+
+
+const struct ieee80211_ops ath5k_hw_ops = {
+ .tx = ath5k_tx,
+ .start = ath5k_start,
+ .stop = ath5k_stop,
+ .add_interface = ath5k_add_interface,
+ /* .change_interface = not implemented */
+ .remove_interface = ath5k_remove_interface,
+ .config = ath5k_config,
+ .bss_info_changed = ath5k_bss_info_changed,
+ .prepare_multicast = ath5k_prepare_multicast,
+ .configure_filter = ath5k_configure_filter,
+ /* .set_tim = not implemented */
+ .set_key = ath5k_set_key,
+ /* .update_tkip_key = not implemented */
+ /* .hw_scan = not implemented */
+ .sw_scan_start = ath5k_sw_scan_start,
+ .sw_scan_complete = ath5k_sw_scan_complete,
+ .get_stats = ath5k_get_stats,
+ /* .get_tkip_seq = not implemented */
+ /* .set_frag_threshold = not implemented */
+ /* .set_rts_threshold = not implemented */
+ /* .sta_add = not implemented */
+ /* .sta_remove = not implemented */
+ /* .sta_notify = not implemented */
+ .conf_tx = ath5k_conf_tx,
+ .get_tsf = ath5k_get_tsf,
+ .set_tsf = ath5k_set_tsf,
+ .reset_tsf = ath5k_reset_tsf,
+ /* .tx_last_beacon = not implemented */
+ /* .ampdu_action = not needed */
+ .get_survey = ath5k_get_survey,
+ .set_coverage_class = ath5k_set_coverage_class,
+ /* .rfkill_poll = not implemented */
+ /* .flush = not implemented */
+ /* .channel_switch = not implemented */
+ /* .napi_poll = not implemented */
+ .set_antenna = ath5k_set_antenna,
+ .get_antenna = ath5k_get_antenna,
+ .set_ringparam = ath5k_set_ringparam,
+ .get_ringparam = ath5k_get_ringparam,
+};
diff --git a/drivers/net/wireless/ath/ath5k/pci.c b/drivers/net/wireless/ath/ath5k/pci.c
new file mode 100644
index 0000000..f90980f
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/pci.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications 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 <linux/nl80211.h>
+#include <linux/pci.h>
+#include <linux/pci-aspm.h>
+#include <linux/etherdevice.h>
+#include <linux/module.h>
+#include "../ath.h"
+#include "ath5k.h"
+#include "debug.h"
+#include "base.h"
+#include "reg.h"
+
+/* Known PCI ids */
+static DEFINE_PCI_DEVICE_TABLE(ath5k_pci_id_table) = {
+ { PCI_VDEVICE(ATHEROS, 0x0207) }, /* 5210 early */
+ { PCI_VDEVICE(ATHEROS, 0x0007) }, /* 5210 */
+ { PCI_VDEVICE(ATHEROS, 0x0011) }, /* 5311 - this is on AHB bus !*/
+ { PCI_VDEVICE(ATHEROS, 0x0012) }, /* 5211 */
+ { PCI_VDEVICE(ATHEROS, 0x0013) }, /* 5212 */
+ { PCI_VDEVICE(3COM_2, 0x0013) }, /* 3com 5212 */
+ { PCI_VDEVICE(3COM, 0x0013) }, /* 3com 3CRDAG675 5212 */
+ { PCI_VDEVICE(ATHEROS, 0x1014) }, /* IBM minipci 5212 */
+ { PCI_VDEVICE(ATHEROS, 0x0014) }, /* 5212 compatible */
+ { PCI_VDEVICE(ATHEROS, 0x0015) }, /* 5212 compatible */
+ { PCI_VDEVICE(ATHEROS, 0x0016) }, /* 5212 compatible */
+ { PCI_VDEVICE(ATHEROS, 0x0017) }, /* 5212 compatible */
+ { PCI_VDEVICE(ATHEROS, 0x0018) }, /* 5212 compatible */
+ { PCI_VDEVICE(ATHEROS, 0x0019) }, /* 5212 compatible */
+ { PCI_VDEVICE(ATHEROS, 0x001a) }, /* 2413 Griffin-lite */
+ { PCI_VDEVICE(ATHEROS, 0x001b) }, /* 5413 Eagle */
+ { PCI_VDEVICE(ATHEROS, 0x001c) }, /* PCI-E cards */
+ { PCI_VDEVICE(ATHEROS, 0x001d) }, /* 2417 Nala */
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, ath5k_pci_id_table);
+
+/* return bus cachesize in 4B word units */
+static void ath5k_pci_read_cachesize(struct ath_common *common, int *csz)
+{
+ struct ath5k_hw *ah = (struct ath5k_hw *) common->priv;
+ u8 u8tmp;
+
+ pci_read_config_byte(ah->pdev, PCI_CACHE_LINE_SIZE, &u8tmp);
+ *csz = (int)u8tmp;
+
+ /*
+ * This check was put in to avoid "unpleasant" consequences if
+ * the bootrom has not fully initialized all PCI devices.
+ * Sometimes the cache line size register is not set
+ */
+
+ if (*csz == 0)
+ *csz = L1_CACHE_BYTES >> 2; /* Use the default size */
+}
+
+/*
+ * Read from eeprom
+ */
+static bool
+ath5k_pci_eeprom_read(struct ath_common *common, u32 offset, u16 *data)
+{
+ struct ath5k_hw *ah = (struct ath5k_hw *) common->ah;
+ u32 status, timeout;
+
+ /*
+ * Initialize EEPROM access
+ */
+ if (ah->ah_version == AR5K_AR5210) {
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_EEAE);
+ (void)ath5k_hw_reg_read(ah, AR5K_EEPROM_BASE + (4 * offset));
+ } else {
+ ath5k_hw_reg_write(ah, offset, AR5K_EEPROM_BASE);
+ AR5K_REG_ENABLE_BITS(ah, AR5K_EEPROM_CMD,
+ AR5K_EEPROM_CMD_READ);
+ }
+
+ for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) {
+ status = ath5k_hw_reg_read(ah, AR5K_EEPROM_STATUS);
+ if (status & AR5K_EEPROM_STAT_RDDONE) {
+ if (status & AR5K_EEPROM_STAT_RDERR)
+ return false;
+ *data = (u16)(ath5k_hw_reg_read(ah, AR5K_EEPROM_DATA) &
+ 0xffff);
+ return true;
+ }
+ udelay(15);
+ }
+
+ return false;
+}
+
+int ath5k_hw_read_srev(struct ath5k_hw *ah)
+{
+ ah->ah_mac_srev = ath5k_hw_reg_read(ah, AR5K_SREV);
+ return 0;
+}
+
+/*
+ * Read the MAC address from eeprom or platform_data
+ */
+static int ath5k_pci_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac)
+{
+ u8 mac_d[ETH_ALEN] = {};
+ u32 total, offset;
+ u16 data;
+ int octet;
+
+ AR5K_EEPROM_READ(0x20, data);
+
+ for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) {
+ AR5K_EEPROM_READ(offset, data);
+
+ total += data;
+ mac_d[octet + 1] = data & 0xff;
+ mac_d[octet] = data >> 8;
+ octet += 2;
+ }
+
+ if (!total || total == 3 * 0xffff)
+ return -EINVAL;
+
+ memcpy(mac, mac_d, ETH_ALEN);
+
+ return 0;
+}
+
+
+/* Common ath_bus_opts structure */
+static const struct ath_bus_ops ath_pci_bus_ops = {
+ .ath_bus_type = ATH_PCI,
+ .read_cachesize = ath5k_pci_read_cachesize,
+ .eeprom_read = ath5k_pci_eeprom_read,
+ .eeprom_read_mac = ath5k_pci_eeprom_read_mac,
+};
+
+/********************\
+* PCI Initialization *
+\********************/
+
+static int __devinit
+ath5k_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ void __iomem *mem;
+ struct ath5k_hw *ah;
+ struct ieee80211_hw *hw;
+ int ret;
+ u8 csz;
+
+ /*
+ * L0s needs to be disabled on all ath5k cards.
+ *
+ * For distributions shipping with CONFIG_PCIEASPM (this will be enabled
+ * by default in the future in 2.6.36) this will also mean both L1 and
+ * L0s will be disabled when a pre 1.1 PCIe device is detected. We do
+ * know L1 works correctly even for all ath5k pre 1.1 PCIe devices
+ * though but cannot currently undue the effect of a blacklist, for
+ * details you can read pcie_aspm_sanity_check() and see how it adjusts
+ * the device link capability.
+ *
+ * It may be possible in the future to implement some PCI API to allow
+ * drivers to override blacklists for pre 1.1 PCIe but for now it is
+ * best to accept that both L0s and L1 will be disabled completely for
+ * distributions shipping with CONFIG_PCIEASPM rather than having this
+ * issue present. Motivation for adding this new API will be to help
+ * with power consumption for some of these devices.
+ */
+ pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S);
+
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "can't enable device\n");
+ goto err;
+ }
+
+ /* XXX 32-bit addressing only */
+ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(&pdev->dev, "32-bit DMA not available\n");
+ goto err_dis;
+ }
+
+ /*
+ * Cache line size is used to size and align various
+ * structures used to communicate with the hardware.
+ */
+ pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz);
+ if (csz == 0) {
+ /*
+ * Linux 2.4.18 (at least) writes the cache line size
+ * register as a 16-bit wide register which is wrong.
+ * We must have this setup properly for rx buffer
+ * DMA to work so force a reasonable value here if it
+ * comes up zero.
+ */
+ csz = L1_CACHE_BYTES >> 2;
+ pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz);
+ }
+ /*
+ * The default setting of latency timer yields poor results,
+ * set it to the value used by other systems. It may be worth
+ * tweaking this setting more.
+ */
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
+
+ /* Enable bus mastering */
+ pci_set_master(pdev);
+
+ /*
+ * Disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state.
+ */
+ pci_write_config_byte(pdev, 0x41, 0);
+
+ ret = pci_request_region(pdev, 0, "ath5k");
+ if (ret) {
+ dev_err(&pdev->dev, "cannot reserve PCI memory region\n");
+ goto err_dis;
+ }
+
+ mem = pci_iomap(pdev, 0, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "cannot remap PCI memory region\n");
+ ret = -EIO;
+ goto err_reg;
+ }
+
+ /*
+ * Allocate hw (mac80211 main struct)
+ * and hw->priv (driver private data)
+ */
+ hw = ieee80211_alloc_hw(sizeof(*ah), &ath5k_hw_ops);
+ if (hw == NULL) {
+ dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n");
+ ret = -ENOMEM;
+ goto err_map;
+ }
+
+ dev_info(&pdev->dev, "registered as '%s'\n", wiphy_name(hw->wiphy));
+
+ ah = hw->priv;
+ ah->hw = hw;
+ ah->pdev = pdev;
+ ah->dev = &pdev->dev;
+ ah->irq = pdev->irq;
+ ah->devid = id->device;
+ ah->iobase = mem; /* So we can unmap it on detach */
+
+ /* Initialize */
+ ret = ath5k_init_ah(ah, &ath_pci_bus_ops);
+ if (ret)
+ goto err_free;
+
+ /* Set private data */
+ pci_set_drvdata(pdev, hw);
+
+ return 0;
+err_free:
+ ieee80211_free_hw(hw);
+err_map:
+ pci_iounmap(pdev, mem);
+err_reg:
+ pci_release_region(pdev, 0);
+err_dis:
+ pci_disable_device(pdev);
+err:
+ return ret;
+}
+
+static void __devexit
+ath5k_pci_remove(struct pci_dev *pdev)
+{
+ struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+ struct ath5k_hw *ah = hw->priv;
+
+ ath5k_deinit_ah(ah);
+ pci_iounmap(pdev, ah->iobase);
+ pci_release_region(pdev, 0);
+ pci_disable_device(pdev);
+ ieee80211_free_hw(hw);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ath5k_pci_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+ struct ath5k_hw *ah = hw->priv;
+
+ ath5k_led_off(ah);
+ return 0;
+}
+
+static int ath5k_pci_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+ struct ath5k_hw *ah = hw->priv;
+
+ /*
+ * Suspend/Resume resets the PCI configuration space, so we have to
+ * re-disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state
+ */
+ pci_write_config_byte(pdev, 0x41, 0);
+
+ ath5k_led_enable(ah);
+ return 0;
+}
+
+compat_pci_suspend(ath5k_pci_suspend)
+compat_pci_resume(ath5k_pci_resume)
+
+static SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume);
+#define ATH5K_PM_OPS (&ath5k_pm_ops)
+#else
+#define ATH5K_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct pci_driver ath5k_pci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = ath5k_pci_id_table,
+ .probe = ath5k_pci_probe,
+ .remove = __devexit_p(ath5k_pci_remove),
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29))
+ .driver.pm = ATH5K_PM_OPS,
+#elif defined(CONFIG_PM_SLEEP)
+ .suspend = ath5k_pci_suspend_compat,
+ .resume = ath5k_pci_resume_compat,
+#endif
+};
+
+/*
+ * Module init/exit functions
+ */
+static int __init
+init_ath5k_pci(void)
+{
+ int ret;
+
+ ret = pci_register_driver(&ath5k_pci_driver);
+ if (ret) {
+ printk(KERN_ERR "ath5k_pci: can't register pci driver\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit
+exit_ath5k_pci(void)
+{
+ pci_unregister_driver(&ath5k_pci_driver);
+}
+
+module_init(init_ath5k_pci);
+module_exit(exit_ath5k_pci);
diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c
new file mode 100644
index 0000000..a7eafa3
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/pcu.c
@@ -0,0 +1,934 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
+ * Copyright (c) 2007-2008 Matthew W. S. Bell <mentor@madwifi.org>
+ * Copyright (c) 2007-2008 Luis Rodriguez <mcgrof@winlab.rutgers.edu>
+ * Copyright (c) 2007-2008 Pavel Roskin <proski@gnu.org>
+ * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com>
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ */
+
+/*********************************\
+* Protocol Control Unit Functions *
+\*********************************/
+
+#include <asm/unaligned.h>
+
+#include "ath5k.h"
+#include "reg.h"
+#include "debug.h"
+
+/*
+ * AR5212+ can use higher rates for ack transmission
+ * based on current tx rate instead of the base rate.
+ * It does this to better utilize channel usage.
+ * This is a mapping between G rates (that cover both
+ * CCK and OFDM) and ack rates that we use when setting
+ * rate -> duration table. This mapping is hw-based so
+ * don't change anything.
+ *
+ * To enable this functionality we must set
+ * ah->ah_ack_bitrate_high to true else base rate is
+ * used (1Mb for CCK, 6Mb for OFDM).
+ */
+static const unsigned int ack_rates_high[] =
+/* Tx -> ACK */
+/* 1Mb -> 1Mb */ { 0,
+/* 2MB -> 2Mb */ 1,
+/* 5.5Mb -> 2Mb */ 1,
+/* 11Mb -> 2Mb */ 1,
+/* 6Mb -> 6Mb */ 4,
+/* 9Mb -> 6Mb */ 4,
+/* 12Mb -> 12Mb */ 6,
+/* 18Mb -> 12Mb */ 6,
+/* 24Mb -> 24Mb */ 8,
+/* 36Mb -> 24Mb */ 8,
+/* 48Mb -> 24Mb */ 8,
+/* 54Mb -> 24Mb */ 8 };
+
+/*******************\
+* Helper functions *
+\*******************/
+
+/**
+ * ath5k_hw_get_frame_duration - Get tx time of a frame
+ *
+ * @ah: The &struct ath5k_hw
+ * @len: Frame's length in bytes
+ * @rate: The @struct ieee80211_rate
+ *
+ * Calculate tx duration of a frame given it's rate and length
+ * It extends ieee80211_generic_frame_duration for non standard
+ * bwmodes.
+ */
+int ath5k_hw_get_frame_duration(struct ath5k_hw *ah,
+ int len, struct ieee80211_rate *rate, bool shortpre)
+{
+ int sifs, preamble, plcp_bits, sym_time;
+ int bitrate, bits, symbols, symbol_bits;
+ int dur;
+
+ /* Fallback */
+ if (!ah->ah_bwmode) {
+ __le16 raw_dur = ieee80211_generic_frame_duration(ah->hw,
+ NULL, len, rate);
+
+ /* subtract difference between long and short preamble */
+ dur = le16_to_cpu(raw_dur);
+ if (shortpre)
+ dur -= 96;
+
+ return dur;
+ }
+
+ bitrate = rate->bitrate;
+ preamble = AR5K_INIT_OFDM_PREAMPLE_TIME;
+ plcp_bits = AR5K_INIT_OFDM_PLCP_BITS;
+ sym_time = AR5K_INIT_OFDM_SYMBOL_TIME;
+
+ switch (ah->ah_bwmode) {
+ case AR5K_BWMODE_40MHZ:
+ sifs = AR5K_INIT_SIFS_TURBO;
+ preamble = AR5K_INIT_OFDM_PREAMBLE_TIME_MIN;
+ break;
+ case AR5K_BWMODE_10MHZ:
+ sifs = AR5K_INIT_SIFS_HALF_RATE;
+ preamble *= 2;
+ sym_time *= 2;
+ break;
+ case AR5K_BWMODE_5MHZ:
+ sifs = AR5K_INIT_SIFS_QUARTER_RATE;
+ preamble *= 4;
+ sym_time *= 4;
+ break;
+ default:
+ sifs = AR5K_INIT_SIFS_DEFAULT_BG;
+ break;
+ }
+
+ bits = plcp_bits + (len << 3);
+ /* Bit rate is in 100Kbits */
+ symbol_bits = bitrate * sym_time;
+ symbols = DIV_ROUND_UP(bits * 10, symbol_bits);
+
+ dur = sifs + preamble + (sym_time * symbols);
+
+ return dur;
+}
+
+/**
+ * ath5k_hw_get_default_slottime - Get the default slot time for current mode
+ *
+ * @ah: The &struct ath5k_hw
+ */
+unsigned int ath5k_hw_get_default_slottime(struct ath5k_hw *ah)
+{
+ struct ieee80211_channel *channel = ah->ah_current_channel;
+ unsigned int slot_time;
+
+ switch (ah->ah_bwmode) {
+ case AR5K_BWMODE_40MHZ:
+ slot_time = AR5K_INIT_SLOT_TIME_TURBO;
+ break;
+ case AR5K_BWMODE_10MHZ:
+ slot_time = AR5K_INIT_SLOT_TIME_HALF_RATE;
+ break;
+ case AR5K_BWMODE_5MHZ:
+ slot_time = AR5K_INIT_SLOT_TIME_QUARTER_RATE;
+ break;
+ case AR5K_BWMODE_DEFAULT:
+ default:
+ slot_time = AR5K_INIT_SLOT_TIME_DEFAULT;
+ if ((channel->hw_value == AR5K_MODE_11B) && !ah->ah_short_slot)
+ slot_time = AR5K_INIT_SLOT_TIME_B;
+ break;
+ }
+
+ return slot_time;
+}
+
+/**
+ * ath5k_hw_get_default_sifs - Get the default SIFS for current mode
+ *
+ * @ah: The &struct ath5k_hw
+ */
+unsigned int ath5k_hw_get_default_sifs(struct ath5k_hw *ah)
+{
+ struct ieee80211_channel *channel = ah->ah_current_channel;
+ unsigned int sifs;
+
+ switch (ah->ah_bwmode) {
+ case AR5K_BWMODE_40MHZ:
+ sifs = AR5K_INIT_SIFS_TURBO;
+ break;
+ case AR5K_BWMODE_10MHZ:
+ sifs = AR5K_INIT_SIFS_HALF_RATE;
+ break;
+ case AR5K_BWMODE_5MHZ:
+ sifs = AR5K_INIT_SIFS_QUARTER_RATE;
+ break;
+ case AR5K_BWMODE_DEFAULT:
+ sifs = AR5K_INIT_SIFS_DEFAULT_BG;
+ default:
+ if (channel->band == IEEE80211_BAND_5GHZ)
+ sifs = AR5K_INIT_SIFS_DEFAULT_A;
+ break;
+ }
+
+ return sifs;
+}
+
+/**
+ * ath5k_hw_update_mib_counters - Update MIB counters (mac layer statistics)
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Reads MIB counters from PCU and updates sw statistics. Is called after a
+ * MIB interrupt, because one of these counters might have reached their maximum
+ * and triggered the MIB interrupt, to let us read and clear the counter.
+ *
+ * Is called in interrupt context!
+ */
+void ath5k_hw_update_mib_counters(struct ath5k_hw *ah)
+{
+ struct ath5k_statistics *stats = &ah->stats;
+
+ /* Read-And-Clear */
+ stats->ack_fail += ath5k_hw_reg_read(ah, AR5K_ACK_FAIL);
+ stats->rts_fail += ath5k_hw_reg_read(ah, AR5K_RTS_FAIL);
+ stats->rts_ok += ath5k_hw_reg_read(ah, AR5K_RTS_OK);
+ stats->fcs_error += ath5k_hw_reg_read(ah, AR5K_FCS_FAIL);
+ stats->beacons += ath5k_hw_reg_read(ah, AR5K_BEACON_CNT);
+}
+
+
+/******************\
+* ACK/CTS Timeouts *
+\******************/
+
+/**
+ * ath5k_hw_write_rate_duration - fill rate code to duration table
+ *
+ * @ah: the &struct ath5k_hw
+ * @mode: one of enum ath5k_driver_mode
+ *
+ * Write the rate code to duration table upon hw reset. This is a helper for
+ * ath5k_hw_pcu_init(). It seems all this is doing is setting an ACK timeout on
+ * the hardware, based on current mode, for each rate. The rates which are
+ * capable of short preamble (802.11b rates 2Mbps, 5.5Mbps, and 11Mbps) have
+ * different rate code so we write their value twice (one for long preamble
+ * and one for short).
+ *
+ * Note: Band doesn't matter here, if we set the values for OFDM it works
+ * on both a and g modes. So all we have to do is set values for all g rates
+ * that include all OFDM and CCK rates.
+ *
+ */
+static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah)
+{
+ struct ieee80211_rate *rate;
+ unsigned int i;
+ /* 802.11g covers both OFDM and CCK */
+ u8 band = IEEE80211_BAND_2GHZ;
+
+ /* Write rate duration table */
+ for (i = 0; i < ah->sbands[band].n_bitrates; i++) {
+ u32 reg;
+ u16 tx_time;
+
+ if (ah->ah_ack_bitrate_high)
+ rate = &ah->sbands[band].bitrates[ack_rates_high[i]];
+ /* CCK -> 1Mb */
+ else if (i < 4)
+ rate = &ah->sbands[band].bitrates[0];
+ /* OFDM -> 6Mb */
+ else
+ rate = &ah->sbands[band].bitrates[4];
+
+ /* Set ACK timeout */
+ reg = AR5K_RATE_DUR(rate->hw_value);
+
+ /* An ACK frame consists of 10 bytes. If you add the FCS,
+ * which ieee80211_generic_frame_duration() adds,
+ * its 14 bytes. Note we use the control rate and not the
+ * actual rate for this rate. See mac80211 tx.c
+ * ieee80211_duration() for a brief description of
+ * what rate we should choose to TX ACKs. */
+ tx_time = ath5k_hw_get_frame_duration(ah, 10, rate, false);
+
+ ath5k_hw_reg_write(ah, tx_time, reg);
+
+ if (!(rate->flags & IEEE80211_RATE_SHORT_PREAMBLE))
+ continue;
+
+ tx_time = ath5k_hw_get_frame_duration(ah, 10, rate, true);
+ ath5k_hw_reg_write(ah, tx_time,
+ reg + (AR5K_SET_SHORT_PREAMBLE << 2));
+ }
+}
+
+/**
+ * ath5k_hw_set_ack_timeout - Set ACK timeout on PCU
+ *
+ * @ah: The &struct ath5k_hw
+ * @timeout: Timeout in usec
+ */
+static int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout)
+{
+ if (ath5k_hw_clocktoh(ah, AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_ACK))
+ <= timeout)
+ return -EINVAL;
+
+ AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_ACK,
+ ath5k_hw_htoclock(ah, timeout));
+
+ return 0;
+}
+
+/**
+ * ath5k_hw_set_cts_timeout - Set CTS timeout on PCU
+ *
+ * @ah: The &struct ath5k_hw
+ * @timeout: Timeout in usec
+ */
+static int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout)
+{
+ if (ath5k_hw_clocktoh(ah, AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_CTS))
+ <= timeout)
+ return -EINVAL;
+
+ AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_CTS,
+ ath5k_hw_htoclock(ah, timeout));
+
+ return 0;
+}
+
+
+/*******************\
+* RX filter Control *
+\*******************/
+
+/**
+ * ath5k_hw_set_lladdr - Set station id
+ *
+ * @ah: The &struct ath5k_hw
+ * @mac: The card's mac address
+ *
+ * Set station id on hw using the provided mac address
+ */
+int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac)
+{
+ struct ath_common *common = ath5k_hw_common(ah);
+ u32 low_id, high_id;
+ u32 pcu_reg;
+
+ /* Set new station ID */
+ memcpy(common->macaddr, mac, ETH_ALEN);
+
+ pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000;
+
+ low_id = get_unaligned_le32(mac);
+ high_id = get_unaligned_le16(mac + 4);
+
+ ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0);
+ ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1);
+
+ return 0;
+}
+
+/**
+ * ath5k_hw_set_bssid - Set current BSSID on hw
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Sets the current BSSID and BSSID mask we have from the
+ * common struct into the hardware
+ */
+void ath5k_hw_set_bssid(struct ath5k_hw *ah)
+{
+ struct ath_common *common = ath5k_hw_common(ah);
+ u16 tim_offset = 0;
+
+ /*
+ * Set BSSID mask on 5212
+ */
+ if (ah->ah_version == AR5K_AR5212)
+ ath_hw_setbssidmask(common);
+
+ /*
+ * Set BSSID
+ */
+ ath5k_hw_reg_write(ah,
+ get_unaligned_le32(common->curbssid),
+ AR5K_BSS_ID0);
+ ath5k_hw_reg_write(ah,
+ get_unaligned_le16(common->curbssid + 4) |
+ ((common->curaid & 0x3fff) << AR5K_BSS_ID1_AID_S),
+ AR5K_BSS_ID1);
+
+ if (common->curaid == 0) {
+ ath5k_hw_disable_pspoll(ah);
+ return;
+ }
+
+ AR5K_REG_WRITE_BITS(ah, AR5K_BEACON, AR5K_BEACON_TIM,
+ tim_offset ? tim_offset + 4 : 0);
+
+ ath5k_hw_enable_pspoll(ah, NULL, 0);
+}
+
+void ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask)
+{
+ struct ath_common *common = ath5k_hw_common(ah);
+
+ /* Cache bssid mask so that we can restore it
+ * on reset */
+ memcpy(common->bssidmask, mask, ETH_ALEN);
+ if (ah->ah_version == AR5K_AR5212)
+ ath_hw_setbssidmask(common);
+}
+
+/*
+ * Set multicast filter
+ */
+void ath5k_hw_set_mcast_filter(struct ath5k_hw *ah, u32 filter0, u32 filter1)
+{
+ ath5k_hw_reg_write(ah, filter0, AR5K_MCAST_FILTER0);
+ ath5k_hw_reg_write(ah, filter1, AR5K_MCAST_FILTER1);
+}
+
+/**
+ * ath5k_hw_get_rx_filter - Get current rx filter
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Returns the RX filter by reading rx filter and
+ * phy error filter registers. RX filter is used
+ * to set the allowed frame types that PCU will accept
+ * and pass to the driver. For a list of frame types
+ * check out reg.h.
+ */
+u32 ath5k_hw_get_rx_filter(struct ath5k_hw *ah)
+{
+ u32 data, filter = 0;
+
+ filter = ath5k_hw_reg_read(ah, AR5K_RX_FILTER);
+
+ /*Radar detection for 5212*/
+ if (ah->ah_version == AR5K_AR5212) {
+ data = ath5k_hw_reg_read(ah, AR5K_PHY_ERR_FIL);
+
+ if (data & AR5K_PHY_ERR_FIL_RADAR)
+ filter |= AR5K_RX_FILTER_RADARERR;
+ if (data & (AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK))
+ filter |= AR5K_RX_FILTER_PHYERR;
+ }
+
+ return filter;
+}
+
+/**
+ * ath5k_hw_set_rx_filter - Set rx filter
+ *
+ * @ah: The &struct ath5k_hw
+ * @filter: RX filter mask (see reg.h)
+ *
+ * Sets RX filter register and also handles PHY error filter
+ * register on 5212 and newer chips so that we have proper PHY
+ * error reporting.
+ */
+void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter)
+{
+ u32 data = 0;
+
+ /* Set PHY error filter register on 5212*/
+ if (ah->ah_version == AR5K_AR5212) {
+ if (filter & AR5K_RX_FILTER_RADARERR)
+ data |= AR5K_PHY_ERR_FIL_RADAR;
+ if (filter & AR5K_RX_FILTER_PHYERR)
+ data |= AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK;
+ }
+
+ /*
+ * The AR5210 uses promiscuous mode to detect radar activity
+ */
+ if (ah->ah_version == AR5K_AR5210 &&
+ (filter & AR5K_RX_FILTER_RADARERR)) {
+ filter &= ~AR5K_RX_FILTER_RADARERR;
+ filter |= AR5K_RX_FILTER_PROM;
+ }
+
+ /*Zero length DMA (phy error reporting) */
+ if (data)
+ AR5K_REG_ENABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA);
+ else
+ AR5K_REG_DISABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA);
+
+ /*Write RX Filter register*/
+ ath5k_hw_reg_write(ah, filter & 0xff, AR5K_RX_FILTER);
+
+ /*Write PHY error filter register on 5212*/
+ if (ah->ah_version == AR5K_AR5212)
+ ath5k_hw_reg_write(ah, data, AR5K_PHY_ERR_FIL);
+
+}
+
+
+/****************\
+* Beacon control *
+\****************/
+
+#define ATH5K_MAX_TSF_READ 10
+
+/**
+ * ath5k_hw_get_tsf64 - Get the full 64bit TSF
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Returns the current TSF
+ */
+u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah)
+{
+ u32 tsf_lower, tsf_upper1, tsf_upper2;
+ int i;
+ unsigned long flags;
+
+ /* This code is time critical - we don't want to be interrupted here */
+ local_irq_save(flags);
+
+ /*
+ * While reading TSF upper and then lower part, the clock is still
+ * counting (or jumping in case of IBSS merge) so we might get
+ * inconsistent values. To avoid this, we read the upper part again
+ * and check it has not been changed. We make the hypothesis that a
+ * maximum of 3 changes can happens in a row (we use 10 as a safe
+ * value).
+ *
+ * Impact on performance is pretty small, since in most cases, only
+ * 3 register reads are needed.
+ */
+
+ tsf_upper1 = ath5k_hw_reg_read(ah, AR5K_TSF_U32);
+ for (i = 0; i < ATH5K_MAX_TSF_READ; i++) {
+ tsf_lower = ath5k_hw_reg_read(ah, AR5K_TSF_L32);
+ tsf_upper2 = ath5k_hw_reg_read(ah, AR5K_TSF_U32);
+ if (tsf_upper2 == tsf_upper1)
+ break;
+ tsf_upper1 = tsf_upper2;
+ }
+
+ local_irq_restore(flags);
+
+ WARN_ON(i == ATH5K_MAX_TSF_READ);
+
+ return ((u64)tsf_upper1 << 32) | tsf_lower;
+}
+
+/**
+ * ath5k_hw_set_tsf64 - Set a new 64bit TSF
+ *
+ * @ah: The &struct ath5k_hw
+ * @tsf64: The new 64bit TSF
+ *
+ * Sets the new TSF
+ */
+void ath5k_hw_set_tsf64(struct ath5k_hw *ah, u64 tsf64)
+{
+ ath5k_hw_reg_write(ah, tsf64 & 0xffffffff, AR5K_TSF_L32);
+ ath5k_hw_reg_write(ah, (tsf64 >> 32) & 0xffffffff, AR5K_TSF_U32);
+}
+
+/**
+ * ath5k_hw_reset_tsf - Force a TSF reset
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Forces a TSF reset on PCU
+ */
+void ath5k_hw_reset_tsf(struct ath5k_hw *ah)
+{
+ u32 val;
+
+ val = ath5k_hw_reg_read(ah, AR5K_BEACON) | AR5K_BEACON_RESET_TSF;
+
+ /*
+ * Each write to the RESET_TSF bit toggles a hardware internal
+ * signal to reset TSF, but if left high it will cause a TSF reset
+ * on the next chip reset as well. Thus we always write the value
+ * twice to clear the signal.
+ */
+ ath5k_hw_reg_write(ah, val, AR5K_BEACON);
+ ath5k_hw_reg_write(ah, val, AR5K_BEACON);
+}
+
+/*
+ * Initialize beacon timers
+ */
+void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval)
+{
+ u32 timer1, timer2, timer3;
+
+ /*
+ * Set the additional timers by mode
+ */
+ switch (ah->opmode) {
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_STATION:
+ /* In STA mode timer1 is used as next wakeup
+ * timer and timer2 as next CFP duration start
+ * timer. Both in 1/8TUs. */
+ /* TODO: PCF handling */
+ if (ah->ah_version == AR5K_AR5210) {
+ timer1 = 0xffffffff;
+ timer2 = 0xffffffff;
+ } else {
+ timer1 = 0x0000ffff;
+ timer2 = 0x0007ffff;
+ }
+ /* Mark associated AP as PCF incapable for now */
+ AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PCF);
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_ADHOC_BCN_ATIM);
+ default:
+ /* On non-STA modes timer1 is used as next DMA
+ * beacon alert (DBA) timer and timer2 as next
+ * software beacon alert. Both in 1/8TUs. */
+ timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) << 3;
+ timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) << 3;
+ break;
+ }
+
+ /* Timer3 marks the end of our ATIM window
+ * a zero length window is not allowed because
+ * we 'll get no beacons */
+ timer3 = next_beacon + 1;
+
+ /*
+ * Set the beacon register and enable all timers.
+ */
+ /* When in AP or Mesh Point mode zero timer0 to start TSF */
+ if (ah->opmode == NL80211_IFTYPE_AP ||
+ ah->opmode == NL80211_IFTYPE_MESH_POINT)
+ ath5k_hw_reg_write(ah, 0, AR5K_TIMER0);
+
+ ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0);
+ ath5k_hw_reg_write(ah, timer1, AR5K_TIMER1);
+ ath5k_hw_reg_write(ah, timer2, AR5K_TIMER2);
+ ath5k_hw_reg_write(ah, timer3, AR5K_TIMER3);
+
+ /* Force a TSF reset if requested and enable beacons */
+ if (interval & AR5K_BEACON_RESET_TSF)
+ ath5k_hw_reset_tsf(ah);
+
+ ath5k_hw_reg_write(ah, interval & (AR5K_BEACON_PERIOD |
+ AR5K_BEACON_ENABLE),
+ AR5K_BEACON);
+
+ /* Flush any pending BMISS interrupts on ISR by
+ * performing a clear-on-write operation on PISR
+ * register for the BMISS bit (writing a bit on
+ * ISR toggles a reset for that bit and leaves
+ * the remaining bits intact) */
+ if (ah->ah_version == AR5K_AR5210)
+ ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_ISR);
+ else
+ ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_PISR);
+
+ /* TODO: Set enhanced sleep registers on AR5212
+ * based on vif->bss_conf params, until then
+ * disable power save reporting.*/
+ AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PWR_SV);
+
+}
+
+/**
+ * ath5k_check_timer_win - Check if timer B is timer A + window
+ *
+ * @a: timer a (before b)
+ * @b: timer b (after a)
+ * @window: difference between a and b
+ * @intval: timers are increased by this interval
+ *
+ * This helper function checks if timer B is timer A + window and covers
+ * cases where timer A or B might have already been updated or wrapped
+ * around (Timers are 16 bit).
+ *
+ * Returns true if O.K.
+ */
+static inline bool
+ath5k_check_timer_win(int a, int b, int window, int intval)
+{
+ /*
+ * 1.) usually B should be A + window
+ * 2.) A already updated, B not updated yet
+ * 3.) A already updated and has wrapped around
+ * 4.) B has wrapped around
+ */
+ if ((b - a == window) || /* 1.) */
+ (a - b == intval - window) || /* 2.) */
+ ((a | 0x10000) - b == intval - window) || /* 3.) */
+ ((b | 0x10000) - a == window)) /* 4.) */
+ return true; /* O.K. */
+ return false;
+}
+
+/**
+ * ath5k_hw_check_beacon_timers - Check if the beacon timers are correct
+ *
+ * @ah: The &struct ath5k_hw
+ * @intval: beacon interval
+ *
+ * This is a workaround for IBSS mode:
+ *
+ * The need for this function arises from the fact that we have 4 separate
+ * HW timer registers (TIMER0 - TIMER3), which are closely related to the
+ * next beacon target time (NBTT), and that the HW updates these timers
+ * separately based on the current TSF value. The hardware increments each
+ * timer by the beacon interval, when the local TSF converted to TU is equal
+ * to the value stored in the timer.
+ *
+ * The reception of a beacon with the same BSSID can update the local HW TSF
+ * at any time - this is something we can't avoid. If the TSF jumps to a
+ * time which is later than the time stored in a timer, this timer will not
+ * be updated until the TSF in TU wraps around at 16 bit (the size of the
+ * timers) and reaches the time which is stored in the timer.
+ *
+ * The problem is that these timers are closely related to TIMER0 (NBTT) and
+ * that they define a time "window". When the TSF jumps between two timers
+ * (e.g. ATIM and NBTT), the one in the past will be left behind (not
+ * updated), while the one in the future will be updated every beacon
+ * interval. This causes the window to get larger, until the TSF wraps
+ * around as described above and the timer which was left behind gets
+ * updated again. But - because the beacon interval is usually not an exact
+ * divisor of the size of the timers (16 bit), an unwanted "window" between
+ * these timers has developed!
+ *
+ * This is especially important with the ATIM window, because during
+ * the ATIM window only ATIM frames and no data frames are allowed to be
+ * sent, which creates transmission pauses after each beacon. This symptom
+ * has been described as "ramping ping" because ping times increase linearly
+ * for some time and then drop down again. A wrong window on the DMA beacon
+ * timer has the same effect, so we check for these two conditions.
+ *
+ * Returns true if O.K.
+ */
+bool
+ath5k_hw_check_beacon_timers(struct ath5k_hw *ah, int intval)
+{
+ unsigned int nbtt, atim, dma;
+
+ nbtt = ath5k_hw_reg_read(ah, AR5K_TIMER0);
+ atim = ath5k_hw_reg_read(ah, AR5K_TIMER3);
+ dma = ath5k_hw_reg_read(ah, AR5K_TIMER1) >> 3;
+
+ /* NOTE: SWBA is different. Having a wrong window there does not
+ * stop us from sending data and this condition is caught by
+ * other means (SWBA interrupt) */
+
+ if (ath5k_check_timer_win(nbtt, atim, 1, intval) &&
+ ath5k_check_timer_win(dma, nbtt, AR5K_TUNE_DMA_BEACON_RESP,
+ intval))
+ return true; /* O.K. */
+ return false;
+}
+
+/**
+ * ath5k_hw_set_coverage_class - Set IEEE 802.11 coverage class
+ *
+ * @ah: The &struct ath5k_hw
+ * @coverage_class: IEEE 802.11 coverage class number
+ *
+ * Sets IFS intervals and ACK/CTS timeouts for given coverage class.
+ */
+void ath5k_hw_set_coverage_class(struct ath5k_hw *ah, u8 coverage_class)
+{
+ /* As defined by IEEE 802.11-2007 17.3.8.6 */
+ int slot_time = ath5k_hw_get_default_slottime(ah) + 3 * coverage_class;
+ int ack_timeout = ath5k_hw_get_default_sifs(ah) + slot_time;
+ int cts_timeout = ack_timeout;
+
+ ath5k_hw_set_ifs_intervals(ah, slot_time);
+ ath5k_hw_set_ack_timeout(ah, ack_timeout);
+ ath5k_hw_set_cts_timeout(ah, cts_timeout);
+
+ ah->ah_coverage_class = coverage_class;
+}
+
+/***************************\
+* Init/Start/Stop functions *
+\***************************/
+
+/**
+ * ath5k_hw_start_rx_pcu - Start RX engine
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Starts RX engine on PCU so that hw can process RXed frames
+ * (ACK etc).
+ *
+ * NOTE: RX DMA should be already enabled using ath5k_hw_start_rx_dma
+ */
+void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah)
+{
+ AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX);
+}
+
+/**
+ * at5k_hw_stop_rx_pcu - Stop RX engine
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Stops RX engine on PCU
+ */
+void ath5k_hw_stop_rx_pcu(struct ath5k_hw *ah)
+{
+ AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX);
+}
+
+/**
+ * ath5k_hw_set_opmode - Set PCU operating mode
+ *
+ * @ah: The &struct ath5k_hw
+ * @op_mode: &enum nl80211_iftype operating mode
+ *
+ * Configure PCU for the various operating modes (AP/STA etc)
+ */
+int ath5k_hw_set_opmode(struct ath5k_hw *ah, enum nl80211_iftype op_mode)
+{
+ struct ath_common *common = ath5k_hw_common(ah);
+ u32 pcu_reg, beacon_reg, low_id, high_id;
+
+ ATH5K_DBG(ah, ATH5K_DEBUG_MODE, "mode %d\n", op_mode);
+
+ /* Preserve rest settings */
+ pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000;
+ pcu_reg &= ~(AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_AP
+ | AR5K_STA_ID1_KEYSRCH_MODE
+ | (ah->ah_version == AR5K_AR5210 ?
+ (AR5K_STA_ID1_PWR_SV | AR5K_STA_ID1_NO_PSPOLL) : 0));
+
+ beacon_reg = 0;
+
+ switch (op_mode) {
+ case NL80211_IFTYPE_ADHOC:
+ pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_KEYSRCH_MODE;
+ beacon_reg |= AR5K_BCR_ADHOC;
+ if (ah->ah_version == AR5K_AR5210)
+ pcu_reg |= AR5K_STA_ID1_NO_PSPOLL;
+ else
+ AR5K_REG_ENABLE_BITS(ah, AR5K_CFG, AR5K_CFG_IBSS);
+ break;
+
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_MESH_POINT:
+ pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_KEYSRCH_MODE;
+ beacon_reg |= AR5K_BCR_AP;
+ if (ah->ah_version == AR5K_AR5210)
+ pcu_reg |= AR5K_STA_ID1_NO_PSPOLL;
+ else
+ AR5K_REG_DISABLE_BITS(ah, AR5K_CFG, AR5K_CFG_IBSS);
+ break;
+
+ case NL80211_IFTYPE_STATION:
+ pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE
+ | (ah->ah_version == AR5K_AR5210 ?
+ AR5K_STA_ID1_PWR_SV : 0);
+ case NL80211_IFTYPE_MONITOR:
+ pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE
+ | (ah->ah_version == AR5K_AR5210 ?
+ AR5K_STA_ID1_NO_PSPOLL : 0);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * Set PCU registers
+ */
+ low_id = get_unaligned_le32(common->macaddr);
+ high_id = get_unaligned_le16(common->macaddr + 4);
+ ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0);
+ ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1);
+
+ /*
+ * Set Beacon Control Register on 5210
+ */
+ if (ah->ah_version == AR5K_AR5210)
+ ath5k_hw_reg_write(ah, beacon_reg, AR5K_BCR);
+
+ return 0;
+}
+
+void ath5k_hw_pcu_init(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
+ u8 mode)
+{
+ /* Set bssid and bssid mask */
+ ath5k_hw_set_bssid(ah);
+
+ /* Set PCU config */
+ ath5k_hw_set_opmode(ah, op_mode);
+
+ /* Write rate duration table only on AR5212 and if
+ * virtual interface has already been brought up
+ * XXX: rethink this after new mode changes to
+ * mac80211 are integrated */
+ if (ah->ah_version == AR5K_AR5212 &&
+ ah->nvifs)
+ ath5k_hw_write_rate_duration(ah);
+
+ /* Set RSSI/BRSSI thresholds
+ *
+ * Note: If we decide to set this value
+ * dynamically, have in mind that when AR5K_RSSI_THR
+ * register is read it might return 0x40 if we haven't
+ * wrote anything to it plus BMISS RSSI threshold is zeroed.
+ * So doing a save/restore procedure here isn't the right
+ * choice. Instead store it on ath5k_hw */
+ ath5k_hw_reg_write(ah, (AR5K_TUNE_RSSI_THRES |
+ AR5K_TUNE_BMISS_THRES <<
+ AR5K_RSSI_THR_BMISS_S),
+ AR5K_RSSI_THR);
+
+ /* MIC QoS support */
+ if (ah->ah_mac_srev >= AR5K_SREV_AR2413) {
+ ath5k_hw_reg_write(ah, 0x000100aa, AR5K_MIC_QOS_CTL);
+ ath5k_hw_reg_write(ah, 0x00003210, AR5K_MIC_QOS_SEL);
+ }
+
+ /* QoS NOACK Policy */
+ if (ah->ah_version == AR5K_AR5212) {
+ ath5k_hw_reg_write(ah,
+ AR5K_REG_SM(2, AR5K_QOS_NOACK_2BIT_VALUES) |
+ AR5K_REG_SM(5, AR5K_QOS_NOACK_BIT_OFFSET) |
+ AR5K_REG_SM(0, AR5K_QOS_NOACK_BYTE_OFFSET),
+ AR5K_QOS_NOACK);
+ }
+
+ /* Restore slot time and ACK timeouts */
+ if (ah->ah_coverage_class > 0)
+ ath5k_hw_set_coverage_class(ah, ah->ah_coverage_class);
+
+ /* Set ACK bitrate mode (see ack_rates_high) */
+ if (ah->ah_version == AR5K_AR5212) {
+ u32 val = AR5K_STA_ID1_BASE_RATE_11B | AR5K_STA_ID1_ACKCTS_6MB;
+ if (ah->ah_ack_bitrate_high)
+ AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, val);
+ else
+ AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, val);
+ }
+ return;
+}
diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c
new file mode 100644
index 0000000..01cb72d
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/phy.c
@@ -0,0 +1,3457 @@
+/*
+ * PHY functions
+ *
+ * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com>
+ * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com>
+ * Copyright (c) 2008-2009 Felix Fietkau <nbd@openwrt.org>
+ *
+ * Permission to use, copy, modify, and 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 <linux/delay.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+#include "ath5k.h"
+#include "reg.h"
+#include "rfbuffer.h"
+#include "rfgain.h"
+#include "../regd.h"
+
+
+/******************\
+* Helper functions *
+\******************/
+
+/*
+ * Get the PHY Chip revision
+ */
+u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, enum ieee80211_band band)
+{
+ unsigned int i;
+ u32 srev;
+ u16 ret;
+
+ /*
+ * Set the radio chip access register
+ */
+ switch (band) {
+ case IEEE80211_BAND_2GHZ:
+ ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_2GHZ, AR5K_PHY(0));
+ break;
+ case IEEE80211_BAND_5GHZ:
+ ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
+ break;
+ default:
+ return 0;
+ }
+
+ mdelay(2);
+
+ /* ...wait until PHY is ready and read the selected radio revision */
+ ath5k_hw_reg_write(ah, 0x00001c16, AR5K_PHY(0x34));
+
+ for (i = 0; i < 8; i++)
+ ath5k_hw_reg_write(ah, 0x00010000, AR5K_PHY(0x20));
+
+ if (ah->ah_version == AR5K_AR5210) {
+ srev = ath5k_hw_reg_read(ah, AR5K_PHY(256) >> 28) & 0xf;
+ ret = (u16)ath5k_hw_bitswap(srev, 4) + 1;
+ } else {
+ srev = (ath5k_hw_reg_read(ah, AR5K_PHY(0x100)) >> 24) & 0xff;
+ ret = (u16)ath5k_hw_bitswap(((srev & 0xf0) >> 4) |
+ ((srev & 0x0f) << 4), 8);
+ }
+
+ /* Reset to the 5GHz mode */
+ ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
+
+ return ret;
+}
+
+/*
+ * Check if a channel is supported
+ */
+bool ath5k_channel_ok(struct ath5k_hw *ah, struct ieee80211_channel *channel)
+{
+ u16 freq = channel->center_freq;
+
+ /* Check if the channel is in our supported range */
+ if (channel->band == IEEE80211_BAND_2GHZ) {
+ if ((freq >= ah->ah_capabilities.cap_range.range_2ghz_min) &&
+ (freq <= ah->ah_capabilities.cap_range.range_2ghz_max))
+ return true;
+ } else if (channel->band == IEEE80211_BAND_5GHZ)
+ if ((freq >= ah->ah_capabilities.cap_range.range_5ghz_min) &&
+ (freq <= ah->ah_capabilities.cap_range.range_5ghz_max))
+ return true;
+
+ return false;
+}
+
+bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel)
+{
+ u8 refclk_freq;
+
+ if ((ah->ah_radio == AR5K_RF5112) ||
+ (ah->ah_radio == AR5K_RF5413) ||
+ (ah->ah_radio == AR5K_RF2413) ||
+ (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
+ refclk_freq = 40;
+ else
+ refclk_freq = 32;
+
+ if ((channel->center_freq % refclk_freq != 0) &&
+ ((channel->center_freq % refclk_freq < 10) ||
+ (channel->center_freq % refclk_freq > 22)))
+ return true;
+ else
+ return false;
+}
+
+/*
+ * Used to modify RF Banks before writing them to AR5K_RF_BUFFER
+ */
+static unsigned int ath5k_hw_rfb_op(struct ath5k_hw *ah,
+ const struct ath5k_rf_reg *rf_regs,
+ u32 val, u8 reg_id, bool set)
+{
+ const struct ath5k_rf_reg *rfreg = NULL;
+ u8 offset, bank, num_bits, col, position;
+ u16 entry;
+ u32 mask, data, last_bit, bits_shifted, first_bit;
+ u32 *rfb;
+ s32 bits_left;
+ int i;
+
+ data = 0;
+ rfb = ah->ah_rf_banks;
+
+ for (i = 0; i < ah->ah_rf_regs_count; i++) {
+ if (rf_regs[i].index == reg_id) {
+ rfreg = &rf_regs[i];
+ break;
+ }
+ }
+
+ if (rfb == NULL || rfreg == NULL) {
+ ATH5K_PRINTF("Rf register not found!\n");
+ /* should not happen */
+ return 0;
+ }
+
+ bank = rfreg->bank;
+ num_bits = rfreg->field.len;
+ first_bit = rfreg->field.pos;
+ col = rfreg->field.col;
+
+ /* first_bit is an offset from bank's
+ * start. Since we have all banks on
+ * the same array, we use this offset
+ * to mark each bank's start */
+ offset = ah->ah_offset[bank];
+
+ /* Boundary check */
+ if (!(col <= 3 && num_bits <= 32 && first_bit + num_bits <= 319)) {
+ ATH5K_PRINTF("invalid values at offset %u\n", offset);
+ return 0;
+ }
+
+ entry = ((first_bit - 1) / 8) + offset;
+ position = (first_bit - 1) % 8;
+
+ if (set)
+ data = ath5k_hw_bitswap(val, num_bits);
+
+ for (bits_shifted = 0, bits_left = num_bits; bits_left > 0;
+ position = 0, entry++) {
+
+ last_bit = (position + bits_left > 8) ? 8 :
+ position + bits_left;
+
+ mask = (((1 << last_bit) - 1) ^ ((1 << position) - 1)) <<
+ (col * 8);
+
+ if (set) {
+ rfb[entry] &= ~mask;
+ rfb[entry] |= ((data << position) << (col * 8)) & mask;
+ data >>= (8 - position);
+ } else {
+ data |= (((rfb[entry] & mask) >> (col * 8)) >> position)
+ << bits_shifted;
+ bits_shifted += last_bit - position;
+ }
+
+ bits_left -= 8 - position;
+ }
+
+ data = set ? 1 : ath5k_hw_bitswap(data, num_bits);
+
+ return data;
+}
+
+/**
+ * ath5k_hw_write_ofdm_timings - set OFDM timings on AR5212
+ *
+ * @ah: the &struct ath5k_hw
+ * @channel: the currently set channel upon reset
+ *
+ * Write the delta slope coefficient (used on pilot tracking ?) for OFDM
+ * operation on the AR5212 upon reset. This is a helper for ath5k_hw_phy_init.
+ *
+ * Since delta slope is floating point we split it on its exponent and
+ * mantissa and provide these values on hw.
+ *
+ * For more infos i think this patent is related
+ * http://www.freepatentsonline.com/7184495.html
+ */
+static inline int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel)
+{
+ /* Get exponent and mantissa and set it */
+ u32 coef_scaled, coef_exp, coef_man,
+ ds_coef_exp, ds_coef_man, clock;
+
+ BUG_ON(!(ah->ah_version == AR5K_AR5212) ||
+ (channel->hw_value == AR5K_MODE_11B));
+
+ /* Get coefficient
+ * ALGO: coef = (5 * clock / carrier_freq) / 2
+ * we scale coef by shifting clock value by 24 for
+ * better precision since we use integers */
+ switch (ah->ah_bwmode) {
+ case AR5K_BWMODE_40MHZ:
+ clock = 40 * 2;
+ break;
+ case AR5K_BWMODE_10MHZ:
+ clock = 40 / 2;
+ break;
+ case AR5K_BWMODE_5MHZ:
+ clock = 40 / 4;
+ break;
+ default:
+ clock = 40;
+ break;
+ }
+ coef_scaled = ((5 * (clock << 24)) / 2) / channel->center_freq;
+
+ /* Get exponent
+ * ALGO: coef_exp = 14 - highest set bit position */
+ coef_exp = ilog2(coef_scaled);
+
+ /* Doesn't make sense if it's zero*/
+ if (!coef_scaled || !coef_exp)
+ return -EINVAL;
+
+ /* Note: we've shifted coef_scaled by 24 */
+ coef_exp = 14 - (coef_exp - 24);
+
+
+ /* Get mantissa (significant digits)
+ * ALGO: coef_mant = floor(coef_scaled* 2^coef_exp+0.5) */
+ coef_man = coef_scaled +
+ (1 << (24 - coef_exp - 1));
+
+ /* Calculate delta slope coefficient exponent
+ * and mantissa (remove scaling) and set them on hw */
+ ds_coef_man = coef_man >> (24 - coef_exp);
+ ds_coef_exp = coef_exp - 16;
+
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3,
+ AR5K_PHY_TIMING_3_DSC_MAN, ds_coef_man);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3,
+ AR5K_PHY_TIMING_3_DSC_EXP, ds_coef_exp);
+
+ return 0;
+}
+
+int ath5k_hw_phy_disable(struct ath5k_hw *ah)
+{
+ /*Just a try M.F.*/
+ ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT);
+
+ return 0;
+}
+
+/*
+ * Wait for synth to settle
+ */
+static void ath5k_hw_wait_for_synth(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel)
+{
+ /*
+ * On 5211+ read activation -> rx delay
+ * and use it (100ns steps).
+ */
+ if (ah->ah_version != AR5K_AR5210) {
+ u32 delay;
+ delay = ath5k_hw_reg_read(ah, AR5K_PHY_RX_DELAY) &
+ AR5K_PHY_RX_DELAY_M;
+ delay = (channel->hw_value == AR5K_MODE_11B) ?
+ ((delay << 2) / 22) : (delay / 10);
+ if (ah->ah_bwmode == AR5K_BWMODE_10MHZ)
+ delay = delay << 1;
+ if (ah->ah_bwmode == AR5K_BWMODE_5MHZ)
+ delay = delay << 2;
+ /* XXX: /2 on turbo ? Let's be safe
+ * for now */
+ udelay(100 + delay);
+ } else {
+ mdelay(1);
+ }
+}
+
+
+/**********************\
+* RF Gain optimization *
+\**********************/
+
+/*
+ * This code is used to optimize RF gain on different environments
+ * (temperature mostly) based on feedback from a power detector.
+ *
+ * It's only used on RF5111 and RF5112, later RF chips seem to have
+ * auto adjustment on hw -notice they have a much smaller BANK 7 and
+ * no gain optimization ladder-.
+ *
+ * For more infos check out this patent doc
+ * http://www.freepatentsonline.com/7400691.html
+ *
+ * This paper describes power drops as seen on the receiver due to
+ * probe packets
+ * http://www.cnri.dit.ie/publications/ICT08%20-%20Practical%20Issues
+ * %20of%20Power%20Control.pdf
+ *
+ * And this is the MadWiFi bug entry related to the above
+ * http://madwifi-project.org/ticket/1659
+ * with various measurements and diagrams
+ *
+ * TODO: Deal with power drops due to probes by setting an appropriate
+ * tx power on the probe packets ! Make this part of the calibration process.
+ */
+
+/* Initialize ah_gain during attach */
+int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah)
+{
+ /* Initialize the gain optimization values */
+ switch (ah->ah_radio) {
+ case AR5K_RF5111:
+ ah->ah_gain.g_step_idx = rfgain_opt_5111.go_default;
+ ah->ah_gain.g_low = 20;
+ ah->ah_gain.g_high = 35;
+ ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
+ break;
+ case AR5K_RF5112:
+ ah->ah_gain.g_step_idx = rfgain_opt_5112.go_default;
+ ah->ah_gain.g_low = 20;
+ ah->ah_gain.g_high = 85;
+ ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Schedule a gain probe check on the next transmitted packet.
+ * That means our next packet is going to be sent with lower
+ * tx power and a Peak to Average Power Detector (PAPD) will try
+ * to measure the gain.
+ *
+ * XXX: How about forcing a tx packet (bypassing PCU arbitrator etc)
+ * just after we enable the probe so that we don't mess with
+ * standard traffic ? Maybe it's time to use sw interrupts and
+ * a probe tasklet !!!
+ */
+static void ath5k_hw_request_rfgain_probe(struct ath5k_hw *ah)
+{
+
+ /* Skip if gain calibration is inactive or
+ * we already handle a probe request */
+ if (ah->ah_gain.g_state != AR5K_RFGAIN_ACTIVE)
+ return;
+
+ /* Send the packet with 2dB below max power as
+ * patent doc suggest */
+ ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_ofdm - 4,
+ AR5K_PHY_PAPD_PROBE_TXPOWER) |
+ AR5K_PHY_PAPD_PROBE_TX_NEXT, AR5K_PHY_PAPD_PROBE);
+
+ ah->ah_gain.g_state = AR5K_RFGAIN_READ_REQUESTED;
+
+}
+
+/* Calculate gain_F measurement correction
+ * based on the current step for RF5112 rev. 2 */
+static u32 ath5k_hw_rf_gainf_corr(struct ath5k_hw *ah)
+{
+ u32 mix, step;
+ u32 *rf;
+ const struct ath5k_gain_opt *go;
+ const struct ath5k_gain_opt_step *g_step;
+ const struct ath5k_rf_reg *rf_regs;
+
+ /* Only RF5112 Rev. 2 supports it */
+ if ((ah->ah_radio != AR5K_RF5112) ||
+ (ah->ah_radio_5ghz_revision <= AR5K_SREV_RAD_5112A))
+ return 0;
+
+ go = &rfgain_opt_5112;
+ rf_regs = rf_regs_5112a;
+ ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112a);
+
+ g_step = &go->go_step[ah->ah_gain.g_step_idx];
+
+ if (ah->ah_rf_banks == NULL)
+ return 0;
+
+ rf = ah->ah_rf_banks;
+ ah->ah_gain.g_f_corr = 0;
+
+ /* No VGA (Variable Gain Amplifier) override, skip */
+ if (ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXVGA_OVR, false) != 1)
+ return 0;
+
+ /* Mix gain stepping */
+ step = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXGAIN_STEP, false);
+
+ /* Mix gain override */
+ mix = g_step->gos_param[0];
+
+ switch (mix) {
+ case 3:
+ ah->ah_gain.g_f_corr = step * 2;
+ break;
+ case 2:
+ ah->ah_gain.g_f_corr = (step - 5) * 2;
+ break;
+ case 1:
+ ah->ah_gain.g_f_corr = step;
+ break;
+ default:
+ ah->ah_gain.g_f_corr = 0;
+ break;
+ }
+
+ return ah->ah_gain.g_f_corr;
+}
+
+/* Check if current gain_F measurement is in the range of our
+ * power detector windows. If we get a measurement outside range
+ * we know it's not accurate (detectors can't measure anything outside
+ * their detection window) so we must ignore it */
+static bool ath5k_hw_rf_check_gainf_readback(struct ath5k_hw *ah)
+{
+ const struct ath5k_rf_reg *rf_regs;
+ u32 step, mix_ovr, level[4];
+ u32 *rf;
+
+ if (ah->ah_rf_banks == NULL)
+ return false;
+
+ rf = ah->ah_rf_banks;
+
+ if (ah->ah_radio == AR5K_RF5111) {
+
+ rf_regs = rf_regs_5111;
+ ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5111);
+
+ step = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_RFGAIN_STEP,
+ false);
+
+ level[0] = 0;
+ level[1] = (step == 63) ? 50 : step + 4;
+ level[2] = (step != 63) ? 64 : level[0];
+ level[3] = level[2] + 50;
+
+ ah->ah_gain.g_high = level[3] -
+ (step == 63 ? AR5K_GAIN_DYN_ADJUST_HI_MARGIN : -5);
+ ah->ah_gain.g_low = level[0] +
+ (step == 63 ? AR5K_GAIN_DYN_ADJUST_LO_MARGIN : 0);
+ } else {
+
+ rf_regs = rf_regs_5112;
+ ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112);
+
+ mix_ovr = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXVGA_OVR,
+ false);
+
+ level[0] = level[2] = 0;
+
+ if (mix_ovr == 1) {
+ level[1] = level[3] = 83;
+ } else {
+ level[1] = level[3] = 107;
+ ah->ah_gain.g_high = 55;
+ }
+ }
+
+ return (ah->ah_gain.g_current >= level[0] &&
+ ah->ah_gain.g_current <= level[1]) ||
+ (ah->ah_gain.g_current >= level[2] &&
+ ah->ah_gain.g_current <= level[3]);
+}
+
+/* Perform gain_F adjustment by choosing the right set
+ * of parameters from RF gain optimization ladder */
+static s8 ath5k_hw_rf_gainf_adjust(struct ath5k_hw *ah)
+{
+ const struct ath5k_gain_opt *go;
+ const struct ath5k_gain_opt_step *g_step;
+ int ret = 0;
+
+ switch (ah->ah_radio) {
+ case AR5K_RF5111:
+ go = &rfgain_opt_5111;
+ break;
+ case AR5K_RF5112:
+ go = &rfgain_opt_5112;
+ break;
+ default:
+ return 0;
+ }
+
+ g_step = &go->go_step[ah->ah_gain.g_step_idx];
+
+ if (ah->ah_gain.g_current >= ah->ah_gain.g_high) {
+
+ /* Reached maximum */
+ if (ah->ah_gain.g_step_idx == 0)
+ return -1;
+
+ for (ah->ah_gain.g_target = ah->ah_gain.g_current;
+ ah->ah_gain.g_target >= ah->ah_gain.g_high &&
+ ah->ah_gain.g_step_idx > 0;
+ g_step = &go->go_step[ah->ah_gain.g_step_idx])
+ ah->ah_gain.g_target -= 2 *
+ (go->go_step[--(ah->ah_gain.g_step_idx)].gos_gain -
+ g_step->gos_gain);
+
+ ret = 1;
+ goto done;
+ }
+
+ if (ah->ah_gain.g_current <= ah->ah_gain.g_low) {
+
+ /* Reached minimum */
+ if (ah->ah_gain.g_step_idx == (go->go_steps_count - 1))
+ return -2;
+
+ for (ah->ah_gain.g_target = ah->ah_gain.g_current;
+ ah->ah_gain.g_target <= ah->ah_gain.g_low &&
+ ah->ah_gain.g_step_idx < go->go_steps_count - 1;
+ g_step = &go->go_step[ah->ah_gain.g_step_idx])
+ ah->ah_gain.g_target -= 2 *
+ (go->go_step[++ah->ah_gain.g_step_idx].gos_gain -
+ g_step->gos_gain);
+
+ ret = 2;
+ goto done;
+ }
+
+done:
+ ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
+ "ret %d, gain step %u, current gain %u, target gain %u\n",
+ ret, ah->ah_gain.g_step_idx, ah->ah_gain.g_current,
+ ah->ah_gain.g_target);
+
+ return ret;
+}
+
+/* Main callback for thermal RF gain calibration engine
+ * Check for a new gain reading and schedule an adjustment
+ * if needed.
+ *
+ * TODO: Use sw interrupt to schedule reset if gain_F needs
+ * adjustment */
+enum ath5k_rfgain ath5k_hw_gainf_calibrate(struct ath5k_hw *ah)
+{
+ u32 data, type;
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+
+ if (ah->ah_rf_banks == NULL ||
+ ah->ah_gain.g_state == AR5K_RFGAIN_INACTIVE)
+ return AR5K_RFGAIN_INACTIVE;
+
+ /* No check requested, either engine is inactive
+ * or an adjustment is already requested */
+ if (ah->ah_gain.g_state != AR5K_RFGAIN_READ_REQUESTED)
+ goto done;
+
+ /* Read the PAPD (Peak to Average Power Detector)
+ * register */
+ data = ath5k_hw_reg_read(ah, AR5K_PHY_PAPD_PROBE);
+
+ /* No probe is scheduled, read gain_F measurement */
+ if (!(data & AR5K_PHY_PAPD_PROBE_TX_NEXT)) {
+ ah->ah_gain.g_current = data >> AR5K_PHY_PAPD_PROBE_GAINF_S;
+ type = AR5K_REG_MS(data, AR5K_PHY_PAPD_PROBE_TYPE);
+
+ /* If tx packet is CCK correct the gain_F measurement
+ * by cck ofdm gain delta */
+ if (type == AR5K_PHY_PAPD_PROBE_TYPE_CCK) {
+ if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A)
+ ah->ah_gain.g_current +=
+ ee->ee_cck_ofdm_gain_delta;
+ else
+ ah->ah_gain.g_current +=
+ AR5K_GAIN_CCK_PROBE_CORR;
+ }
+
+ /* Further correct gain_F measurement for
+ * RF5112A radios */
+ if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) {
+ ath5k_hw_rf_gainf_corr(ah);
+ ah->ah_gain.g_current =
+ ah->ah_gain.g_current >= ah->ah_gain.g_f_corr ?
+ (ah->ah_gain.g_current - ah->ah_gain.g_f_corr) :
+ 0;
+ }
+
+ /* Check if measurement is ok and if we need
+ * to adjust gain, schedule a gain adjustment,
+ * else switch back to the active state */
+ if (ath5k_hw_rf_check_gainf_readback(ah) &&
+ AR5K_GAIN_CHECK_ADJUST(&ah->ah_gain) &&
+ ath5k_hw_rf_gainf_adjust(ah)) {
+ ah->ah_gain.g_state = AR5K_RFGAIN_NEED_CHANGE;
+ } else {
+ ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
+ }
+ }
+
+done:
+ return ah->ah_gain.g_state;
+}
+
+/* Write initial RF gain table to set the RF sensitivity
+ * this one works on all RF chips and has nothing to do
+ * with gain_F calibration */
+static int ath5k_hw_rfgain_init(struct ath5k_hw *ah, enum ieee80211_band band)
+{
+ const struct ath5k_ini_rfgain *ath5k_rfg;
+ unsigned int i, size, index;
+
+ switch (ah->ah_radio) {
+ case AR5K_RF5111:
+ ath5k_rfg = rfgain_5111;
+ size = ARRAY_SIZE(rfgain_5111);
+ break;
+ case AR5K_RF5112:
+ ath5k_rfg = rfgain_5112;
+ size = ARRAY_SIZE(rfgain_5112);
+ break;
+ case AR5K_RF2413:
+ ath5k_rfg = rfgain_2413;
+ size = ARRAY_SIZE(rfgain_2413);
+ break;
+ case AR5K_RF2316:
+ ath5k_rfg = rfgain_2316;
+ size = ARRAY_SIZE(rfgain_2316);
+ break;
+ case AR5K_RF5413:
+ ath5k_rfg = rfgain_5413;
+ size = ARRAY_SIZE(rfgain_5413);
+ break;
+ case AR5K_RF2317:
+ case AR5K_RF2425:
+ ath5k_rfg = rfgain_2425;
+ size = ARRAY_SIZE(rfgain_2425);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ index = (band == IEEE80211_BAND_2GHZ) ? 1 : 0;
+
+ for (i = 0; i < size; i++) {
+ AR5K_REG_WAIT(i);
+ ath5k_hw_reg_write(ah, ath5k_rfg[i].rfg_value[index],
+ (u32)ath5k_rfg[i].rfg_register);
+ }
+
+ return 0;
+}
+
+
+
+/********************\
+* RF Registers setup *
+\********************/
+
+/*
+ * Setup RF registers by writing RF buffer on hw
+ */
+static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel, unsigned int mode)
+{
+ const struct ath5k_rf_reg *rf_regs;
+ const struct ath5k_ini_rfbuffer *ini_rfb;
+ const struct ath5k_gain_opt *go = NULL;
+ const struct ath5k_gain_opt_step *g_step;
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ u8 ee_mode = 0;
+ u32 *rfb;
+ int i, obdb = -1, bank = -1;
+
+ switch (ah->ah_radio) {
+ case AR5K_RF5111:
+ rf_regs = rf_regs_5111;
+ ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5111);
+ ini_rfb = rfb_5111;
+ ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5111);
+ go = &rfgain_opt_5111;
+ break;
+ case AR5K_RF5112:
+ if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) {
+ rf_regs = rf_regs_5112a;
+ ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112a);
+ ini_rfb = rfb_5112a;
+ ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5112a);
+ } else {
+ rf_regs = rf_regs_5112;
+ ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112);
+ ini_rfb = rfb_5112;
+ ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5112);
+ }
+ go = &rfgain_opt_5112;
+ break;
+ case AR5K_RF2413:
+ rf_regs = rf_regs_2413;
+ ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2413);
+ ini_rfb = rfb_2413;
+ ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2413);
+ break;
+ case AR5K_RF2316:
+ rf_regs = rf_regs_2316;
+ ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2316);
+ ini_rfb = rfb_2316;
+ ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2316);
+ break;
+ case AR5K_RF5413:
+ rf_regs = rf_regs_5413;
+ ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5413);
+ ini_rfb = rfb_5413;
+ ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5413);
+ break;
+ case AR5K_RF2317:
+ rf_regs = rf_regs_2425;
+ ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2425);
+ ini_rfb = rfb_2317;
+ ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2317);
+ break;
+ case AR5K_RF2425:
+ rf_regs = rf_regs_2425;
+ ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2425);
+ if (ah->ah_mac_srev < AR5K_SREV_AR2417) {
+ ini_rfb = rfb_2425;
+ ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2425);
+ } else {
+ ini_rfb = rfb_2417;
+ ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2417);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* If it's the first time we set RF buffer, allocate
+ * ah->ah_rf_banks based on ah->ah_rf_banks_size
+ * we set above */
+ if (ah->ah_rf_banks == NULL) {
+ ah->ah_rf_banks = kmalloc(sizeof(u32) * ah->ah_rf_banks_size,
+ GFP_KERNEL);
+ if (ah->ah_rf_banks == NULL) {
+ ATH5K_ERR(ah, "out of memory\n");
+ return -ENOMEM;
+ }
+ }
+
+ /* Copy values to modify them */
+ rfb = ah->ah_rf_banks;
+
+ for (i = 0; i < ah->ah_rf_banks_size; i++) {
+ if (ini_rfb[i].rfb_bank >= AR5K_MAX_RF_BANKS) {
+ ATH5K_ERR(ah, "invalid bank\n");
+ return -EINVAL;
+ }
+
+ /* Bank changed, write down the offset */
+ if (bank != ini_rfb[i].rfb_bank) {
+ bank = ini_rfb[i].rfb_bank;
+ ah->ah_offset[bank] = i;
+ }
+
+ rfb[i] = ini_rfb[i].rfb_mode_data[mode];
+ }
+
+ /* Set Output and Driver bias current (OB/DB) */
+ if (channel->band == IEEE80211_BAND_2GHZ) {
+
+ if (channel->hw_value == AR5K_MODE_11B)
+ ee_mode = AR5K_EEPROM_MODE_11B;
+ else
+ ee_mode = AR5K_EEPROM_MODE_11G;
+
+ /* For RF511X/RF211X combination we
+ * use b_OB and b_DB parameters stored
+ * in eeprom on ee->ee_ob[ee_mode][0]
+ *
+ * For all other chips we use OB/DB for 2GHz
+ * stored in the b/g modal section just like
+ * 802.11a on ee->ee_ob[ee_mode][1] */
+ if ((ah->ah_radio == AR5K_RF5111) ||
+ (ah->ah_radio == AR5K_RF5112))
+ obdb = 0;
+ else
+ obdb = 1;
+
+ ath5k_hw_rfb_op(ah, rf_regs, ee->ee_ob[ee_mode][obdb],
+ AR5K_RF_OB_2GHZ, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, ee->ee_db[ee_mode][obdb],
+ AR5K_RF_DB_2GHZ, true);
+
+ /* RF5111 always needs OB/DB for 5GHz, even if we use 2GHz */
+ } else if ((channel->band == IEEE80211_BAND_5GHZ) ||
+ (ah->ah_radio == AR5K_RF5111)) {
+
+ /* For 11a, Turbo and XR we need to choose
+ * OB/DB based on frequency range */
+ ee_mode = AR5K_EEPROM_MODE_11A;
+ obdb = channel->center_freq >= 5725 ? 3 :
+ (channel->center_freq >= 5500 ? 2 :
+ (channel->center_freq >= 5260 ? 1 :
+ (channel->center_freq > 4000 ? 0 : -1)));
+
+ if (obdb < 0)
+ return -EINVAL;
+
+ ath5k_hw_rfb_op(ah, rf_regs, ee->ee_ob[ee_mode][obdb],
+ AR5K_RF_OB_5GHZ, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, ee->ee_db[ee_mode][obdb],
+ AR5K_RF_DB_5GHZ, true);
+ }
+
+ g_step = &go->go_step[ah->ah_gain.g_step_idx];
+
+ /* Set turbo mode (N/A on RF5413) */
+ if ((ah->ah_bwmode == AR5K_BWMODE_40MHZ) &&
+ (ah->ah_radio != AR5K_RF5413))
+ ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_TURBO, false);
+
+ /* Bank Modifications (chip-specific) */
+ if (ah->ah_radio == AR5K_RF5111) {
+
+ /* Set gain_F settings according to current step */
+ if (channel->hw_value != AR5K_MODE_11B) {
+
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_FRAME_CTL,
+ AR5K_PHY_FRAME_CTL_TX_CLIP,
+ g_step->gos_param[0]);
+
+ ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[1],
+ AR5K_RF_PWD_90, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[2],
+ AR5K_RF_PWD_84, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[3],
+ AR5K_RF_RFGAIN_SEL, true);
+
+ /* We programmed gain_F parameters, switch back
+ * to active state */
+ ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
+
+ }
+
+ /* Bank 6/7 setup */
+
+ ath5k_hw_rfb_op(ah, rf_regs, !ee->ee_xpd[ee_mode],
+ AR5K_RF_PWD_XPD, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, ee->ee_x_gain[ee_mode],
+ AR5K_RF_XPD_GAIN, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, ee->ee_i_gain[ee_mode],
+ AR5K_RF_GAIN_I, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, ee->ee_xpd[ee_mode],
+ AR5K_RF_PLO_SEL, true);
+
+ /* Tweak power detectors for half/quarter rate support */
+ if (ah->ah_bwmode == AR5K_BWMODE_5MHZ ||
+ ah->ah_bwmode == AR5K_BWMODE_10MHZ) {
+ u8 wait_i;
+
+ ath5k_hw_rfb_op(ah, rf_regs, 0x1f,
+ AR5K_RF_WAIT_S, true);
+
+ wait_i = (ah->ah_bwmode == AR5K_BWMODE_5MHZ) ?
+ 0x1f : 0x10;
+
+ ath5k_hw_rfb_op(ah, rf_regs, wait_i,
+ AR5K_RF_WAIT_I, true);
+ ath5k_hw_rfb_op(ah, rf_regs, 3,
+ AR5K_RF_MAX_TIME, true);
+
+ }
+ }
+
+ if (ah->ah_radio == AR5K_RF5112) {
+
+ /* Set gain_F settings according to current step */
+ if (channel->hw_value != AR5K_MODE_11B) {
+
+ ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[0],
+ AR5K_RF_MIXGAIN_OVR, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[1],
+ AR5K_RF_PWD_138, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[2],
+ AR5K_RF_PWD_137, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[3],
+ AR5K_RF_PWD_136, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[4],
+ AR5K_RF_PWD_132, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[5],
+ AR5K_RF_PWD_131, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[6],
+ AR5K_RF_PWD_130, true);
+
+ /* We programmed gain_F parameters, switch back
+ * to active state */
+ ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
+ }
+
+ /* Bank 6/7 setup */
+
+ ath5k_hw_rfb_op(ah, rf_regs, ee->ee_xpd[ee_mode],
+ AR5K_RF_XPD_SEL, true);
+
+ if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112A) {
+ /* Rev. 1 supports only one xpd */
+ ath5k_hw_rfb_op(ah, rf_regs,
+ ee->ee_x_gain[ee_mode],
+ AR5K_RF_XPD_GAIN, true);
+
+ } else {
+ u8 *pdg_curve_to_idx = ee->ee_pdc_to_idx[ee_mode];
+ if (ee->ee_pd_gains[ee_mode] > 1) {
+ ath5k_hw_rfb_op(ah, rf_regs,
+ pdg_curve_to_idx[0],
+ AR5K_RF_PD_GAIN_LO, true);
+ ath5k_hw_rfb_op(ah, rf_regs,
+ pdg_curve_to_idx[1],
+ AR5K_RF_PD_GAIN_HI, true);
+ } else {
+ ath5k_hw_rfb_op(ah, rf_regs,
+ pdg_curve_to_idx[0],
+ AR5K_RF_PD_GAIN_LO, true);
+ ath5k_hw_rfb_op(ah, rf_regs,
+ pdg_curve_to_idx[0],
+ AR5K_RF_PD_GAIN_HI, true);
+ }
+
+ /* Lower synth voltage on Rev 2 */
+ if (ah->ah_radio == AR5K_RF5112 &&
+ (ah->ah_radio_5ghz_revision & AR5K_SREV_REV) > 0) {
+ ath5k_hw_rfb_op(ah, rf_regs, 2,
+ AR5K_RF_HIGH_VC_CP, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, 2,
+ AR5K_RF_MID_VC_CP, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, 2,
+ AR5K_RF_LOW_VC_CP, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, 2,
+ AR5K_RF_PUSH_UP, true);
+ }
+
+ /* Decrease power consumption on 5213+ BaseBand */
+ if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
+ ath5k_hw_rfb_op(ah, rf_regs, 1,
+ AR5K_RF_PAD2GND, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, 1,
+ AR5K_RF_XB2_LVL, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, 1,
+ AR5K_RF_XB5_LVL, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, 1,
+ AR5K_RF_PWD_167, true);
+
+ ath5k_hw_rfb_op(ah, rf_regs, 1,
+ AR5K_RF_PWD_166, true);
+ }
+ }
+
+ ath5k_hw_rfb_op(ah, rf_regs, ee->ee_i_gain[ee_mode],
+ AR5K_RF_GAIN_I, true);
+
+ /* Tweak power detector for half/quarter rates */
+ if (ah->ah_bwmode == AR5K_BWMODE_5MHZ ||
+ ah->ah_bwmode == AR5K_BWMODE_10MHZ) {
+ u8 pd_delay;
+
+ pd_delay = (ah->ah_bwmode == AR5K_BWMODE_5MHZ) ?
+ 0xf : 0x8;
+
+ ath5k_hw_rfb_op(ah, rf_regs, pd_delay,
+ AR5K_RF_PD_PERIOD_A, true);
+ ath5k_hw_rfb_op(ah, rf_regs, 0xf,
+ AR5K_RF_PD_DELAY_A, true);
+
+ }
+ }
+
+ if (ah->ah_radio == AR5K_RF5413 &&
+ channel->band == IEEE80211_BAND_2GHZ) {
+
+ ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_DERBY_CHAN_SEL_MODE,
+ true);
+
+ /* Set optimum value for early revisions (on pci-e chips) */
+ if (ah->ah_mac_srev >= AR5K_SREV_AR5424 &&
+ ah->ah_mac_srev < AR5K_SREV_AR5413)
+ ath5k_hw_rfb_op(ah, rf_regs, ath5k_hw_bitswap(6, 3),
+ AR5K_RF_PWD_ICLOBUF_2G, true);
+
+ }
+
+ /* Write RF banks on hw */
+ for (i = 0; i < ah->ah_rf_banks_size; i++) {
+ AR5K_REG_WAIT(i);
+ ath5k_hw_reg_write(ah, rfb[i], ini_rfb[i].rfb_ctrl_register);
+ }
+
+ return 0;
+}
+
+
+/**************************\
+ PHY/RF channel functions
+\**************************/
+
+/*
+ * Conversion needed for RF5110
+ */
+static u32 ath5k_hw_rf5110_chan2athchan(struct ieee80211_channel *channel)
+{
+ u32 athchan;
+
+ /*
+ * Convert IEEE channel/MHz to an internal channel value used
+ * by the AR5210 chipset. This has not been verified with
+ * newer chipsets like the AR5212A who have a completely
+ * different RF/PHY part.
+ */
+ athchan = (ath5k_hw_bitswap(
+ (ieee80211_frequency_to_channel(
+ channel->center_freq) - 24) / 2, 5)
+ << 1) | (1 << 6) | 0x1;
+ return athchan;
+}
+
+/*
+ * Set channel on RF5110
+ */
+static int ath5k_hw_rf5110_channel(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel)
+{
+ u32 data;
+
+ /*
+ * Set the channel and wait
+ */
+ data = ath5k_hw_rf5110_chan2athchan(channel);
+ ath5k_hw_reg_write(ah, data, AR5K_RF_BUFFER);
+ ath5k_hw_reg_write(ah, 0, AR5K_RF_BUFFER_CONTROL_0);
+ mdelay(1);
+
+ return 0;
+}
+
+/*
+ * Conversion needed for 5111
+ */
+static int ath5k_hw_rf5111_chan2athchan(unsigned int ieee,
+ struct ath5k_athchan_2ghz *athchan)
+{
+ int channel;
+
+ /* Cast this value to catch negative channel numbers (>= -19) */
+ channel = (int)ieee;
+
+ /*
+ * Map 2GHz IEEE channel to 5GHz Atheros channel
+ */
+ if (channel <= 13) {
+ athchan->a2_athchan = 115 + channel;
+ athchan->a2_flags = 0x46;
+ } else if (channel == 14) {
+ athchan->a2_athchan = 124;
+ athchan->a2_flags = 0x44;
+ } else if (channel >= 15 && channel <= 26) {
+ athchan->a2_athchan = ((channel - 14) * 4) + 132;
+ athchan->a2_flags = 0x46;
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
+ * Set channel on 5111
+ */
+static int ath5k_hw_rf5111_channel(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel)
+{
+ struct ath5k_athchan_2ghz ath5k_channel_2ghz;
+ unsigned int ath5k_channel =
+ ieee80211_frequency_to_channel(channel->center_freq);
+ u32 data0, data1, clock;
+ int ret;
+
+ /*
+ * Set the channel on the RF5111 radio
+ */
+ data0 = data1 = 0;
+
+ if (channel->band == IEEE80211_BAND_2GHZ) {
+ /* Map 2GHz channel to 5GHz Atheros channel ID */
+ ret = ath5k_hw_rf5111_chan2athchan(
+ ieee80211_frequency_to_channel(channel->center_freq),
+ &ath5k_channel_2ghz);
+ if (ret)
+ return ret;
+
+ ath5k_channel = ath5k_channel_2ghz.a2_athchan;
+ data0 = ((ath5k_hw_bitswap(ath5k_channel_2ghz.a2_flags, 8) & 0xff)
+ << 5) | (1 << 4);
+ }
+
+ if (ath5k_channel < 145 || !(ath5k_channel & 1)) {
+ clock = 1;
+ data1 = ((ath5k_hw_bitswap(ath5k_channel - 24, 8) & 0xff) << 2) |
+ (clock << 1) | (1 << 10) | 1;
+ } else {
+ clock = 0;
+ data1 = ((ath5k_hw_bitswap((ath5k_channel - 24) / 2, 8) & 0xff)
+ << 2) | (clock << 1) | (1 << 10) | 1;
+ }
+
+ ath5k_hw_reg_write(ah, (data1 & 0xff) | ((data0 & 0xff) << 8),
+ AR5K_RF_BUFFER);
+ ath5k_hw_reg_write(ah, ((data1 >> 8) & 0xff) | (data0 & 0xff00),
+ AR5K_RF_BUFFER_CONTROL_3);
+
+ return 0;
+}
+
+/*
+ * Set channel on 5112 and newer
+ */
+static int ath5k_hw_rf5112_channel(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel)
+{
+ u32 data, data0, data1, data2;
+ u16 c;
+
+ data = data0 = data1 = data2 = 0;
+ c = channel->center_freq;
+
+ if (c < 4800) {
+ if (!((c - 2224) % 5)) {
+ data0 = ((2 * (c - 704)) - 3040) / 10;
+ data1 = 1;
+ } else if (!((c - 2192) % 5)) {
+ data0 = ((2 * (c - 672)) - 3040) / 10;
+ data1 = 0;
+ } else
+ return -EINVAL;
+
+ data0 = ath5k_hw_bitswap((data0 << 2) & 0xff, 8);
+ } else if ((c % 5) != 2 || c > 5435) {
+ if (!(c % 20) && c >= 5120) {
+ data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8);
+ data2 = ath5k_hw_bitswap(3, 2);
+ } else if (!(c % 10)) {
+ data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8);
+ data2 = ath5k_hw_bitswap(2, 2);
+ } else if (!(c % 5)) {
+ data0 = ath5k_hw_bitswap((c - 4800) / 5, 8);
+ data2 = ath5k_hw_bitswap(1, 2);
+ } else
+ return -EINVAL;
+ } else {
+ data0 = ath5k_hw_bitswap((10 * (c - 2 - 4800)) / 25 + 1, 8);
+ data2 = ath5k_hw_bitswap(0, 2);
+ }
+
+ data = (data0 << 4) | (data1 << 1) | (data2 << 2) | 0x1001;
+
+ ath5k_hw_reg_write(ah, data & 0xff, AR5K_RF_BUFFER);
+ ath5k_hw_reg_write(ah, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5);
+
+ return 0;
+}
+
+/*
+ * Set the channel on the RF2425
+ */
+static int ath5k_hw_rf2425_channel(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel)
+{
+ u32 data, data0, data2;
+ u16 c;
+
+ data = data0 = data2 = 0;
+ c = channel->center_freq;
+
+ if (c < 4800) {
+ data0 = ath5k_hw_bitswap((c - 2272), 8);
+ data2 = 0;
+ /* ? 5GHz ? */
+ } else if ((c % 5) != 2 || c > 5435) {
+ if (!(c % 20) && c < 5120)
+ data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8);
+ else if (!(c % 10))
+ data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8);
+ else if (!(c % 5))
+ data0 = ath5k_hw_bitswap((c - 4800) / 5, 8);
+ else
+ return -EINVAL;
+ data2 = ath5k_hw_bitswap(1, 2);
+ } else {
+ data0 = ath5k_hw_bitswap((10 * (c - 2 - 4800)) / 25 + 1, 8);
+ data2 = ath5k_hw_bitswap(0, 2);
+ }
+
+ data = (data0 << 4) | data2 << 2 | 0x1001;
+
+ ath5k_hw_reg_write(ah, data & 0xff, AR5K_RF_BUFFER);
+ ath5k_hw_reg_write(ah, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5);
+
+ return 0;
+}
+
+/*
+ * Set a channel on the radio chip
+ */
+static int ath5k_hw_channel(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel)
+{
+ int ret;
+ /*
+ * Check bounds supported by the PHY (we don't care about regulatory
+ * restrictions at this point).
+ */
+ if (!ath5k_channel_ok(ah, channel)) {
+ ATH5K_ERR(ah,
+ "channel frequency (%u MHz) out of supported "
+ "band range\n",
+ channel->center_freq);
+ return -EINVAL;
+ }
+
+ /*
+ * Set the channel and wait
+ */
+ switch (ah->ah_radio) {
+ case AR5K_RF5110:
+ ret = ath5k_hw_rf5110_channel(ah, channel);
+ break;
+ case AR5K_RF5111:
+ ret = ath5k_hw_rf5111_channel(ah, channel);
+ break;
+ case AR5K_RF2317:
+ case AR5K_RF2425:
+ ret = ath5k_hw_rf2425_channel(ah, channel);
+ break;
+ default:
+ ret = ath5k_hw_rf5112_channel(ah, channel);
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ /* Set JAPAN setting for channel 14 */
+ if (channel->center_freq == 2484) {
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL,
+ AR5K_PHY_CCKTXCTL_JAPAN);
+ } else {
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL,
+ AR5K_PHY_CCKTXCTL_WORLD);
+ }
+
+ ah->ah_current_channel = channel;
+
+ return 0;
+}
+
+/*****************\
+ PHY calibration
+\*****************/
+
+static s32 ath5k_hw_read_measured_noise_floor(struct ath5k_hw *ah)
+{
+ s32 val;
+
+ val = ath5k_hw_reg_read(ah, AR5K_PHY_NF);
+ return sign_extend32(AR5K_REG_MS(val, AR5K_PHY_NF_MINCCA_PWR), 8);
+}
+
+void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah)
+{
+ int i;
+
+ ah->ah_nfcal_hist.index = 0;
+ for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++)
+ ah->ah_nfcal_hist.nfval[i] = AR5K_TUNE_CCA_MAX_GOOD_VALUE;
+}
+
+static void ath5k_hw_update_nfcal_hist(struct ath5k_hw *ah, s16 noise_floor)
+{
+ struct ath5k_nfcal_hist *hist = &ah->ah_nfcal_hist;
+ hist->index = (hist->index + 1) & (ATH5K_NF_CAL_HIST_MAX - 1);
+ hist->nfval[hist->index] = noise_floor;
+}
+
+static s16 ath5k_hw_get_median_noise_floor(struct ath5k_hw *ah)
+{
+ s16 sort[ATH5K_NF_CAL_HIST_MAX];
+ s16 tmp;
+ int i, j;
+
+ memcpy(sort, ah->ah_nfcal_hist.nfval, sizeof(sort));
+ for (i = 0; i < ATH5K_NF_CAL_HIST_MAX - 1; i++) {
+ for (j = 1; j < ATH5K_NF_CAL_HIST_MAX - i; j++) {
+ if (sort[j] > sort[j - 1]) {
+ tmp = sort[j];
+ sort[j] = sort[j - 1];
+ sort[j - 1] = tmp;
+ }
+ }
+ }
+ for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++) {
+ ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
+ "cal %d:%d\n", i, sort[i]);
+ }
+ return sort[(ATH5K_NF_CAL_HIST_MAX - 1) / 2];
+}
+
+/*
+ * When we tell the hardware to perform a noise floor calibration
+ * by setting the AR5K_PHY_AGCCTL_NF bit, it will periodically
+ * sample-and-hold the minimum noise level seen at the antennas.
+ * This value is then stored in a ring buffer of recently measured
+ * noise floor values so we have a moving window of the last few
+ * samples.
+ *
+ * The median of the values in the history is then loaded into the
+ * hardware for its own use for RSSI and CCA measurements.
+ */
+void ath5k_hw_update_noise_floor(struct ath5k_hw *ah)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ u32 val;
+ s16 nf, threshold;
+ u8 ee_mode;
+
+ /* keep last value if calibration hasn't completed */
+ if (ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL) & AR5K_PHY_AGCCTL_NF) {
+ ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
+ "NF did not complete in calibration window\n");
+
+ return;
+ }
+
+ ee_mode = ath5k_eeprom_mode_from_channel(ah->ah_current_channel);
+
+ /* completed NF calibration, test threshold */
+ nf = ath5k_hw_read_measured_noise_floor(ah);
+ threshold = ee->ee_noise_floor_thr[ee_mode];
+
+ if (nf > threshold) {
+ ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
+ "noise floor failure detected; "
+ "read %d, threshold %d\n",
+ nf, threshold);
+
+ nf = AR5K_TUNE_CCA_MAX_GOOD_VALUE;
+ }
+
+ ath5k_hw_update_nfcal_hist(ah, nf);
+ nf = ath5k_hw_get_median_noise_floor(ah);
+
+ /* load noise floor (in .5 dBm) so the hardware will use it */
+ val = ath5k_hw_reg_read(ah, AR5K_PHY_NF) & ~AR5K_PHY_NF_M;
+ val |= (nf * 2) & AR5K_PHY_NF_M;
+ ath5k_hw_reg_write(ah, val, AR5K_PHY_NF);
+
+ AR5K_REG_MASKED_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF,
+ ~(AR5K_PHY_AGCCTL_NF_EN | AR5K_PHY_AGCCTL_NF_NOUPDATE));
+
+ ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF,
+ 0, false);
+
+ /*
+ * Load a high max CCA Power value (-50 dBm in .5 dBm units)
+ * so that we're not capped by the median we just loaded.
+ * This will be used as the initial value for the next noise
+ * floor calibration.
+ */
+ val = (val & ~AR5K_PHY_NF_M) | ((-50 * 2) & AR5K_PHY_NF_M);
+ ath5k_hw_reg_write(ah, val, AR5K_PHY_NF);
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
+ AR5K_PHY_AGCCTL_NF_EN |
+ AR5K_PHY_AGCCTL_NF_NOUPDATE |
+ AR5K_PHY_AGCCTL_NF);
+
+ ah->ah_noise_floor = nf;
+
+ ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
+ "noise floor calibrated: %d\n", nf);
+}
+
+/*
+ * Perform a PHY calibration on RF5110
+ * -Fix BPSK/QAM Constellation (I/Q correction)
+ */
+static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel)
+{
+ u32 phy_sig, phy_agc, phy_sat, beacon;
+ int ret;
+
+ /*
+ * Disable beacons and RX/TX queues, wait
+ */
+ AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5210,
+ AR5K_DIAG_SW_DIS_TX_5210 | AR5K_DIAG_SW_DIS_RX_5210);
+ beacon = ath5k_hw_reg_read(ah, AR5K_BEACON_5210);
+ ath5k_hw_reg_write(ah, beacon & ~AR5K_BEACON_ENABLE, AR5K_BEACON_5210);
+
+ mdelay(2);
+
+ /*
+ * Set the channel (with AGC turned off)
+ */
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
+ udelay(10);
+ ret = ath5k_hw_channel(ah, channel);
+
+ /*
+ * Activate PHY and wait
+ */
+ ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT);
+ mdelay(1);
+
+ AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
+
+ if (ret)
+ return ret;
+
+ /*
+ * Calibrate the radio chip
+ */
+
+ /* Remember normal state */
+ phy_sig = ath5k_hw_reg_read(ah, AR5K_PHY_SIG);
+ phy_agc = ath5k_hw_reg_read(ah, AR5K_PHY_AGCCOARSE);
+ phy_sat = ath5k_hw_reg_read(ah, AR5K_PHY_ADCSAT);
+
+ /* Update radio registers */
+ ath5k_hw_reg_write(ah, (phy_sig & ~(AR5K_PHY_SIG_FIRPWR)) |
+ AR5K_REG_SM(-1, AR5K_PHY_SIG_FIRPWR), AR5K_PHY_SIG);
+
+ ath5k_hw_reg_write(ah, (phy_agc & ~(AR5K_PHY_AGCCOARSE_HI |
+ AR5K_PHY_AGCCOARSE_LO)) |
+ AR5K_REG_SM(-1, AR5K_PHY_AGCCOARSE_HI) |
+ AR5K_REG_SM(-127, AR5K_PHY_AGCCOARSE_LO), AR5K_PHY_AGCCOARSE);
+
+ ath5k_hw_reg_write(ah, (phy_sat & ~(AR5K_PHY_ADCSAT_ICNT |
+ AR5K_PHY_ADCSAT_THR)) |
+ AR5K_REG_SM(2, AR5K_PHY_ADCSAT_ICNT) |
+ AR5K_REG_SM(12, AR5K_PHY_ADCSAT_THR), AR5K_PHY_ADCSAT);
+
+ udelay(20);
+
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
+ udelay(10);
+ ath5k_hw_reg_write(ah, AR5K_PHY_RFSTG_DISABLE, AR5K_PHY_RFSTG);
+ AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
+
+ mdelay(1);
+
+ /*
+ * Enable calibration and wait until completion
+ */
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_CAL);
+
+ ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL,
+ AR5K_PHY_AGCCTL_CAL, 0, false);
+
+ /* Reset to normal state */
+ ath5k_hw_reg_write(ah, phy_sig, AR5K_PHY_SIG);
+ ath5k_hw_reg_write(ah, phy_agc, AR5K_PHY_AGCCOARSE);
+ ath5k_hw_reg_write(ah, phy_sat, AR5K_PHY_ADCSAT);
+
+ if (ret) {
+ ATH5K_ERR(ah, "calibration timeout (%uMHz)\n",
+ channel->center_freq);
+ return ret;
+ }
+
+ /*
+ * Re-enable RX/TX and beacons
+ */
+ AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5210,
+ AR5K_DIAG_SW_DIS_TX_5210 | AR5K_DIAG_SW_DIS_RX_5210);
+ ath5k_hw_reg_write(ah, beacon, AR5K_BEACON_5210);
+
+ return 0;
+}
+
+/*
+ * Perform I/Q calibration on RF5111/5112 and newer chips
+ */
+static int
+ath5k_hw_rf511x_iq_calibrate(struct ath5k_hw *ah)
+{
+ u32 i_pwr, q_pwr;
+ s32 iq_corr, i_coff, i_coffd, q_coff, q_coffd;
+ int i;
+
+ if (!ah->ah_calibration ||
+ ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & AR5K_PHY_IQ_RUN)
+ return 0;
+
+ /* Calibration has finished, get the results and re-run */
+ /* work around empty results which can apparently happen on 5212 */
+ for (i = 0; i <= 10; i++) {
+ iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR);
+ i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I);
+ q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q);
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE,
+ "iq_corr:%x i_pwr:%x q_pwr:%x", iq_corr, i_pwr, q_pwr);
+ if (i_pwr && q_pwr)
+ break;
+ }
+
+ i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7;
+
+ if (ah->ah_version == AR5K_AR5211)
+ q_coffd = q_pwr >> 6;
+ else
+ q_coffd = q_pwr >> 7;
+
+ /* protect against divide by 0 and loss of sign bits */
+ if (i_coffd == 0 || q_coffd < 2)
+ return 0;
+
+ i_coff = (-iq_corr) / i_coffd;
+ i_coff = clamp(i_coff, -32, 31); /* signed 6 bit */
+
+ if (ah->ah_version == AR5K_AR5211)
+ q_coff = (i_pwr / q_coffd) - 64;
+ else
+ q_coff = (i_pwr / q_coffd) - 128;
+ q_coff = clamp(q_coff, -16, 15); /* signed 5 bit */
+
+ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE,
+ "new I:%d Q:%d (i_coffd:%x q_coffd:%x)",
+ i_coff, q_coff, i_coffd, q_coffd);
+
+ /* Commit new I/Q values (set enable bit last to match HAL sources) */
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_I_COFF, i_coff);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_Q_COFF, q_coff);
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE);
+
+ /* Re-enable calibration -if we don't we'll commit
+ * the same values again and again */
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ,
+ AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15);
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_RUN);
+
+ return 0;
+}
+
+/*
+ * Perform a PHY calibration
+ */
+int ath5k_hw_phy_calibrate(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel)
+{
+ int ret;
+
+ if (ah->ah_radio == AR5K_RF5110)
+ return ath5k_hw_rf5110_calibrate(ah, channel);
+
+ ret = ath5k_hw_rf511x_iq_calibrate(ah);
+
+ if ((ah->ah_radio == AR5K_RF5111 || ah->ah_radio == AR5K_RF5112) &&
+ (channel->hw_value != AR5K_MODE_11B))
+ ath5k_hw_request_rfgain_probe(ah);
+
+ return ret;
+}
+
+
+/***************************\
+* Spur mitigation functions *
+\***************************/
+
+static void
+ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ u32 mag_mask[4] = {0, 0, 0, 0};
+ u32 pilot_mask[2] = {0, 0};
+ /* Note: fbin values are scaled up by 2 */
+ u16 spur_chan_fbin, chan_fbin, symbol_width, spur_detection_window;
+ s32 spur_delta_phase, spur_freq_sigma_delta;
+ s32 spur_offset, num_symbols_x16;
+ u8 num_symbol_offsets, i, freq_band;
+
+ /* Convert current frequency to fbin value (the same way channels
+ * are stored on EEPROM, check out ath5k_eeprom_bin2freq) and scale
+ * up by 2 so we can compare it later */
+ if (channel->band == IEEE80211_BAND_2GHZ) {
+ chan_fbin = (channel->center_freq - 2300) * 10;
+ freq_band = AR5K_EEPROM_BAND_2GHZ;
+ } else {
+ chan_fbin = (channel->center_freq - 4900) * 10;
+ freq_band = AR5K_EEPROM_BAND_5GHZ;
+ }
+
+ /* Check if any spur_chan_fbin from EEPROM is
+ * within our current channel's spur detection range */
+ spur_chan_fbin = AR5K_EEPROM_NO_SPUR;
+ spur_detection_window = AR5K_SPUR_CHAN_WIDTH;
+ /* XXX: Half/Quarter channels ?*/
+ if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
+ spur_detection_window *= 2;
+
+ for (i = 0; i < AR5K_EEPROM_N_SPUR_CHANS; i++) {
+ spur_chan_fbin = ee->ee_spur_chans[i][freq_band];
+
+ /* Note: mask cleans AR5K_EEPROM_NO_SPUR flag
+ * so it's zero if we got nothing from EEPROM */
+ if (spur_chan_fbin == AR5K_EEPROM_NO_SPUR) {
+ spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK;
+ break;
+ }
+
+ if ((chan_fbin - spur_detection_window <=
+ (spur_chan_fbin & AR5K_EEPROM_SPUR_CHAN_MASK)) &&
+ (chan_fbin + spur_detection_window >=
+ (spur_chan_fbin & AR5K_EEPROM_SPUR_CHAN_MASK))) {
+ spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK;
+ break;
+ }
+ }
+
+ /* We need to enable spur filter for this channel */
+ if (spur_chan_fbin) {
+ spur_offset = spur_chan_fbin - chan_fbin;
+ /*
+ * Calculate deltas:
+ * spur_freq_sigma_delta -> spur_offset / sample_freq << 21
+ * spur_delta_phase -> spur_offset / chip_freq << 11
+ * Note: Both values have 100Hz resolution
+ */
+ switch (ah->ah_bwmode) {
+ case AR5K_BWMODE_40MHZ:
+ /* Both sample_freq and chip_freq are 80MHz */
+ spur_delta_phase = (spur_offset << 16) / 25;
+ spur_freq_sigma_delta = (spur_delta_phase >> 10);
+ symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz * 2;
+ break;
+ case AR5K_BWMODE_10MHZ:
+ /* Both sample_freq and chip_freq are 20MHz (?) */
+ spur_delta_phase = (spur_offset << 18) / 25;
+ spur_freq_sigma_delta = (spur_delta_phase >> 10);
+ symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 2;
+ case AR5K_BWMODE_5MHZ:
+ /* Both sample_freq and chip_freq are 10MHz (?) */
+ spur_delta_phase = (spur_offset << 19) / 25;
+ spur_freq_sigma_delta = (spur_delta_phase >> 10);
+ symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 4;
+ default:
+ if (channel->band == IEEE80211_BAND_5GHZ) {
+ /* Both sample_freq and chip_freq are 40MHz */
+ spur_delta_phase = (spur_offset << 17) / 25;
+ spur_freq_sigma_delta =
+ (spur_delta_phase >> 10);
+ symbol_width =
+ AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz;
+ } else {
+ /* sample_freq -> 40MHz chip_freq -> 44MHz
+ * (for b compatibility) */
+ spur_delta_phase = (spur_offset << 17) / 25;
+ spur_freq_sigma_delta =
+ (spur_offset << 8) / 55;
+ symbol_width =
+ AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz;
+ }
+ break;
+ }
+
+ /* Calculate pilot and magnitude masks */
+
+ /* Scale up spur_offset by 1000 to switch to 100HZ resolution
+ * and divide by symbol_width to find how many symbols we have
+ * Note: number of symbols is scaled up by 16 */
+ num_symbols_x16 = ((spur_offset * 1000) << 4) / symbol_width;
+
+ /* Spur is on a symbol if num_symbols_x16 % 16 is zero */
+ if (!(num_symbols_x16 & 0xF))
+ /* _X_ */
+ num_symbol_offsets = 3;
+ else
+ /* _xx_ */
+ num_symbol_offsets = 4;
+
+ for (i = 0; i < num_symbol_offsets; i++) {
+
+ /* Calculate pilot mask */
+ s32 curr_sym_off =
+ (num_symbols_x16 / 16) + i + 25;
+
+ /* Pilot magnitude mask seems to be a way to
+ * declare the boundaries for our detection
+ * window or something, it's 2 for the middle
+ * value(s) where the symbol is expected to be
+ * and 1 on the boundary values */
+ u8 plt_mag_map =
+ (i == 0 || i == (num_symbol_offsets - 1))
+ ? 1 : 2;
+
+ if (curr_sym_off >= 0 && curr_sym_off <= 32) {
+ if (curr_sym_off <= 25)
+ pilot_mask[0] |= 1 << curr_sym_off;
+ else if (curr_sym_off >= 27)
+ pilot_mask[0] |= 1 << (curr_sym_off - 1);
+ } else if (curr_sym_off >= 33 && curr_sym_off <= 52)
+ pilot_mask[1] |= 1 << (curr_sym_off - 33);
+
+ /* Calculate magnitude mask (for viterbi decoder) */
+ if (curr_sym_off >= -1 && curr_sym_off <= 14)
+ mag_mask[0] |=
+ plt_mag_map << (curr_sym_off + 1) * 2;
+ else if (curr_sym_off >= 15 && curr_sym_off <= 30)
+ mag_mask[1] |=
+ plt_mag_map << (curr_sym_off - 15) * 2;
+ else if (curr_sym_off >= 31 && curr_sym_off <= 46)
+ mag_mask[2] |=
+ plt_mag_map << (curr_sym_off - 31) * 2;
+ else if (curr_sym_off >= 47 && curr_sym_off <= 53)
+ mag_mask[3] |=
+ plt_mag_map << (curr_sym_off - 47) * 2;
+
+ }
+
+ /* Write settings on hw to enable spur filter */
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
+ AR5K_PHY_BIN_MASK_CTL_RATE, 0xff);
+ /* XXX: Self correlator also ? */
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ,
+ AR5K_PHY_IQ_PILOT_MASK_EN |
+ AR5K_PHY_IQ_CHAN_MASK_EN |
+ AR5K_PHY_IQ_SPUR_FILT_EN);
+
+ /* Set delta phase and freq sigma delta */
+ ath5k_hw_reg_write(ah,
+ AR5K_REG_SM(spur_delta_phase,
+ AR5K_PHY_TIMING_11_SPUR_DELTA_PHASE) |
+ AR5K_REG_SM(spur_freq_sigma_delta,
+ AR5K_PHY_TIMING_11_SPUR_FREQ_SD) |
+ AR5K_PHY_TIMING_11_USE_SPUR_IN_AGC,
+ AR5K_PHY_TIMING_11);
+
+ /* Write pilot masks */
+ ath5k_hw_reg_write(ah, pilot_mask[0], AR5K_PHY_TIMING_7);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_8,
+ AR5K_PHY_TIMING_8_PILOT_MASK_2,
+ pilot_mask[1]);
+
+ ath5k_hw_reg_write(ah, pilot_mask[0], AR5K_PHY_TIMING_9);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_10,
+ AR5K_PHY_TIMING_10_PILOT_MASK_2,
+ pilot_mask[1]);
+
+ /* Write magnitude masks */
+ ath5k_hw_reg_write(ah, mag_mask[0], AR5K_PHY_BIN_MASK_1);
+ ath5k_hw_reg_write(ah, mag_mask[1], AR5K_PHY_BIN_MASK_2);
+ ath5k_hw_reg_write(ah, mag_mask[2], AR5K_PHY_BIN_MASK_3);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
+ AR5K_PHY_BIN_MASK_CTL_MASK_4,
+ mag_mask[3]);
+
+ ath5k_hw_reg_write(ah, mag_mask[0], AR5K_PHY_BIN_MASK2_1);
+ ath5k_hw_reg_write(ah, mag_mask[1], AR5K_PHY_BIN_MASK2_2);
+ ath5k_hw_reg_write(ah, mag_mask[2], AR5K_PHY_BIN_MASK2_3);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK2_4,
+ AR5K_PHY_BIN_MASK2_4_MASK_4,
+ mag_mask[3]);
+
+ } else if (ath5k_hw_reg_read(ah, AR5K_PHY_IQ) &
+ AR5K_PHY_IQ_SPUR_FILT_EN) {
+ /* Clean up spur mitigation settings and disable filter */
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
+ AR5K_PHY_BIN_MASK_CTL_RATE, 0);
+ AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_IQ,
+ AR5K_PHY_IQ_PILOT_MASK_EN |
+ AR5K_PHY_IQ_CHAN_MASK_EN |
+ AR5K_PHY_IQ_SPUR_FILT_EN);
+ ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_11);
+
+ /* Clear pilot masks */
+ ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_7);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_8,
+ AR5K_PHY_TIMING_8_PILOT_MASK_2,
+ 0);
+
+ ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_9);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_10,
+ AR5K_PHY_TIMING_10_PILOT_MASK_2,
+ 0);
+
+ /* Clear magnitude masks */
+ ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_1);
+ ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_2);
+ ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_3);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
+ AR5K_PHY_BIN_MASK_CTL_MASK_4,
+ 0);
+
+ ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_1);
+ ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_2);
+ ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_3);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK2_4,
+ AR5K_PHY_BIN_MASK2_4_MASK_4,
+ 0);
+ }
+}
+
+
+/*****************\
+* Antenna control *
+\*****************/
+
+static void /*TODO:Boundary check*/
+ath5k_hw_set_def_antenna(struct ath5k_hw *ah, u8 ant)
+{
+ if (ah->ah_version != AR5K_AR5210)
+ ath5k_hw_reg_write(ah, ant & 0x7, AR5K_DEFAULT_ANTENNA);
+}
+
+/*
+ * Enable/disable fast rx antenna diversity
+ */
+static void
+ath5k_hw_set_fast_div(struct ath5k_hw *ah, u8 ee_mode, bool enable)
+{
+ switch (ee_mode) {
+ case AR5K_EEPROM_MODE_11G:
+ /* XXX: This is set to
+ * disabled on initvals !!! */
+ case AR5K_EEPROM_MODE_11A:
+ if (enable)
+ AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGCCTL,
+ AR5K_PHY_AGCCTL_OFDM_DIV_DIS);
+ else
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
+ AR5K_PHY_AGCCTL_OFDM_DIV_DIS);
+ break;
+ case AR5K_EEPROM_MODE_11B:
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
+ AR5K_PHY_AGCCTL_OFDM_DIV_DIS);
+ break;
+ default:
+ return;
+ }
+
+ if (enable) {
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RESTART,
+ AR5K_PHY_RESTART_DIV_GC, 4);
+
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_FAST_ANT_DIV,
+ AR5K_PHY_FAST_ANT_DIV_EN);
+ } else {
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RESTART,
+ AR5K_PHY_RESTART_DIV_GC, 0);
+
+ AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_FAST_ANT_DIV,
+ AR5K_PHY_FAST_ANT_DIV_EN);
+ }
+}
+
+void
+ath5k_hw_set_antenna_switch(struct ath5k_hw *ah, u8 ee_mode)
+{
+ u8 ant0, ant1;
+
+ /*
+ * In case a fixed antenna was set as default
+ * use the same switch table twice.
+ */
+ if (ah->ah_ant_mode == AR5K_ANTMODE_FIXED_A)
+ ant0 = ant1 = AR5K_ANT_SWTABLE_A;
+ else if (ah->ah_ant_mode == AR5K_ANTMODE_FIXED_B)
+ ant0 = ant1 = AR5K_ANT_SWTABLE_B;
+ else {
+ ant0 = AR5K_ANT_SWTABLE_A;
+ ant1 = AR5K_ANT_SWTABLE_B;
+ }
+
+ /* Set antenna idle switch table */
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_ANT_CTL,
+ AR5K_PHY_ANT_CTL_SWTABLE_IDLE,
+ (ah->ah_ant_ctl[ee_mode][AR5K_ANT_CTL] |
+ AR5K_PHY_ANT_CTL_TXRX_EN));
+
+ /* Set antenna switch tables */
+ ath5k_hw_reg_write(ah, ah->ah_ant_ctl[ee_mode][ant0],
+ AR5K_PHY_ANT_SWITCH_TABLE_0);
+ ath5k_hw_reg_write(ah, ah->ah_ant_ctl[ee_mode][ant1],
+ AR5K_PHY_ANT_SWITCH_TABLE_1);
+}
+
+/*
+ * Set antenna operating mode
+ */
+void
+ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode)
+{
+ struct ieee80211_channel *channel = ah->ah_current_channel;
+ bool use_def_for_tx, update_def_on_tx, use_def_for_rts, fast_div;
+ bool use_def_for_sg;
+ int ee_mode;
+ u8 def_ant, tx_ant;
+ u32 sta_id1 = 0;
+
+ /* if channel is not initialized yet we can't set the antennas
+ * so just store the mode. it will be set on the next reset */
+ if (channel == NULL) {
+ ah->ah_ant_mode = ant_mode;
+ return;
+ }
+
+ def_ant = ah->ah_def_ant;
+
+ ee_mode = ath5k_eeprom_mode_from_channel(channel);
+ if (ee_mode < 0) {
+ ATH5K_ERR(ah,
+ "invalid channel: %d\n", channel->center_freq);
+ return;
+ }
+
+ switch (ant_mode) {
+ case AR5K_ANTMODE_DEFAULT:
+ tx_ant = 0;
+ use_def_for_tx = false;
+ update_def_on_tx = false;
+ use_def_for_rts = false;
+ use_def_for_sg = false;
+ fast_div = true;
+ break;
+ case AR5K_ANTMODE_FIXED_A:
+ def_ant = 1;
+ tx_ant = 1;
+ use_def_for_tx = true;
+ update_def_on_tx = false;
+ use_def_for_rts = true;
+ use_def_for_sg = true;
+ fast_div = false;
+ break;
+ case AR5K_ANTMODE_FIXED_B:
+ def_ant = 2;
+ tx_ant = 2;
+ use_def_for_tx = true;
+ update_def_on_tx = false;
+ use_def_for_rts = true;
+ use_def_for_sg = true;
+ fast_div = false;
+ break;
+ case AR5K_ANTMODE_SINGLE_AP:
+ def_ant = 1; /* updated on tx */
+ tx_ant = 0;
+ use_def_for_tx = true;
+ update_def_on_tx = true;
+ use_def_for_rts = true;
+ use_def_for_sg = true;
+ fast_div = true;
+ break;
+ case AR5K_ANTMODE_SECTOR_AP:
+ tx_ant = 1; /* variable */
+ use_def_for_tx = false;
+ update_def_on_tx = false;
+ use_def_for_rts = true;
+ use_def_for_sg = false;
+ fast_div = false;
+ break;
+ case AR5K_ANTMODE_SECTOR_STA:
+ tx_ant = 1; /* variable */
+ use_def_for_tx = true;
+ update_def_on_tx = false;
+ use_def_for_rts = true;
+ use_def_for_sg = false;
+ fast_div = true;
+ break;
+ case AR5K_ANTMODE_DEBUG:
+ def_ant = 1;
+ tx_ant = 2;
+ use_def_for_tx = false;
+ update_def_on_tx = false;
+ use_def_for_rts = false;
+ use_def_for_sg = false;
+ fast_div = false;
+ break;
+ default:
+ return;
+ }
+
+ ah->ah_tx_ant = tx_ant;
+ ah->ah_ant_mode = ant_mode;
+ ah->ah_def_ant = def_ant;
+
+ sta_id1 |= use_def_for_tx ? AR5K_STA_ID1_DEFAULT_ANTENNA : 0;
+ sta_id1 |= update_def_on_tx ? AR5K_STA_ID1_DESC_ANTENNA : 0;
+ sta_id1 |= use_def_for_rts ? AR5K_STA_ID1_RTS_DEF_ANTENNA : 0;
+ sta_id1 |= use_def_for_sg ? AR5K_STA_ID1_SELFGEN_DEF_ANT : 0;
+
+ AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_ANTENNA_SETTINGS);
+
+ if (sta_id1)
+ AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, sta_id1);
+
+ ath5k_hw_set_antenna_switch(ah, ee_mode);
+ /* Note: set diversity before default antenna
+ * because it won't work correctly */
+ ath5k_hw_set_fast_div(ah, ee_mode, fast_div);
+ ath5k_hw_set_def_antenna(ah, def_ant);
+}
+
+
+/****************\
+* TX power setup *
+\****************/
+
+/*
+ * Helper functions
+ */
+
+/*
+ * Do linear interpolation between two given (x, y) points
+ */
+static s16
+ath5k_get_interpolated_value(s16 target, s16 x_left, s16 x_right,
+ s16 y_left, s16 y_right)
+{
+ s16 ratio, result;
+
+ /* Avoid divide by zero and skip interpolation
+ * if we have the same point */
+ if ((x_left == x_right) || (y_left == y_right))
+ return y_left;
+
+ /*
+ * Since we use ints and not fps, we need to scale up in
+ * order to get a sane ratio value (or else we 'll eg. get
+ * always 1 instead of 1.25, 1.75 etc). We scale up by 100
+ * to have some accuracy both for 0.5 and 0.25 steps.
+ */
+ ratio = ((100 * y_right - 100 * y_left) / (x_right - x_left));
+
+ /* Now scale down to be in range */
+ result = y_left + (ratio * (target - x_left) / 100);
+
+ return result;
+}
+
+/*
+ * Find vertical boundary (min pwr) for the linear PCDAC curve.
+ *
+ * Since we have the top of the curve and we draw the line below
+ * until we reach 1 (1 pcdac step) we need to know which point
+ * (x value) that is so that we don't go below y axis and have negative
+ * pcdac values when creating the curve, or fill the table with zeroes.
+ */
+static s16
+ath5k_get_linear_pcdac_min(const u8 *stepL, const u8 *stepR,
+ const s16 *pwrL, const s16 *pwrR)
+{
+ s8 tmp;
+ s16 min_pwrL, min_pwrR;
+ s16 pwr_i;
+
+ /* Some vendors write the same pcdac value twice !!! */
+ if (stepL[0] == stepL[1] || stepR[0] == stepR[1])
+ return max(pwrL[0], pwrR[0]);
+
+ if (pwrL[0] == pwrL[1])
+ min_pwrL = pwrL[0];
+ else {
+ pwr_i = pwrL[0];
+ do {
+ pwr_i--;
+ tmp = (s8) ath5k_get_interpolated_value(pwr_i,
+ pwrL[0], pwrL[1],
+ stepL[0], stepL[1]);
+ } while (tmp > 1);
+
+ min_pwrL = pwr_i;
+ }
+
+ if (pwrR[0] == pwrR[1])
+ min_pwrR = pwrR[0];
+ else {
+ pwr_i = pwrR[0];
+ do {
+ pwr_i--;
+ tmp = (s8) ath5k_get_interpolated_value(pwr_i,
+ pwrR[0], pwrR[1],
+ stepR[0], stepR[1]);
+ } while (tmp > 1);
+
+ min_pwrR = pwr_i;
+ }
+
+ /* Keep the right boundary so that it works for both curves */
+ return max(min_pwrL, min_pwrR);
+}
+
+/*
+ * Interpolate (pwr,vpd) points to create a Power to PDADC or a
+ * Power to PCDAC curve.
+ *
+ * Each curve has power on x axis (in 0.5dB units) and PCDAC/PDADC
+ * steps (offsets) on y axis. Power can go up to 31.5dB and max
+ * PCDAC/PDADC step for each curve is 64 but we can write more than
+ * one curves on hw so we can go up to 128 (which is the max step we
+ * can write on the final table).
+ *
+ * We write y values (PCDAC/PDADC steps) on hw.
+ */
+static void
+ath5k_create_power_curve(s16 pmin, s16 pmax,
+ const s16 *pwr, const u8 *vpd,
+ u8 num_points,
+ u8 *vpd_table, u8 type)
+{
+ u8 idx[2] = { 0, 1 };
+ s16 pwr_i = 2 * pmin;
+ int i;
+
+ if (num_points < 2)
+ return;
+
+ /* We want the whole line, so adjust boundaries
+ * to cover the entire power range. Note that
+ * power values are already 0.25dB so no need
+ * to multiply pwr_i by 2 */
+ if (type == AR5K_PWRTABLE_LINEAR_PCDAC) {
+ pwr_i = pmin;
+ pmin = 0;
+ pmax = 63;
+ }
+
+ /* Find surrounding turning points (TPs)
+ * and interpolate between them */
+ for (i = 0; (i <= (u16) (pmax - pmin)) &&
+ (i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) {
+
+ /* We passed the right TP, move to the next set of TPs
+ * if we pass the last TP, extrapolate above using the last
+ * two TPs for ratio */
+ if ((pwr_i > pwr[idx[1]]) && (idx[1] < num_points - 1)) {
+ idx[0]++;
+ idx[1]++;
+ }
+
+ vpd_table[i] = (u8) ath5k_get_interpolated_value(pwr_i,
+ pwr[idx[0]], pwr[idx[1]],
+ vpd[idx[0]], vpd[idx[1]]);
+
+ /* Increase by 0.5dB
+ * (0.25 dB units) */
+ pwr_i += 2;
+ }
+}
+
+/*
+ * Get the surrounding per-channel power calibration piers
+ * for a given frequency so that we can interpolate between
+ * them and come up with an appropriate dataset for our current
+ * channel.
+ */
+static void
+ath5k_get_chan_pcal_surrounding_piers(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel,
+ struct ath5k_chan_pcal_info **pcinfo_l,
+ struct ath5k_chan_pcal_info **pcinfo_r)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info *pcinfo;
+ u8 idx_l, idx_r;
+ u8 mode, max, i;
+ u32 target = channel->center_freq;
+
+ idx_l = 0;
+ idx_r = 0;
+
+ switch (channel->hw_value) {
+ case AR5K_EEPROM_MODE_11A:
+ pcinfo = ee->ee_pwr_cal_a;
+ mode = AR5K_EEPROM_MODE_11A;
+ break;
+ case AR5K_EEPROM_MODE_11B:
+ pcinfo = ee->ee_pwr_cal_b;
+ mode = AR5K_EEPROM_MODE_11B;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ default:
+ pcinfo = ee->ee_pwr_cal_g;
+ mode = AR5K_EEPROM_MODE_11G;
+ break;
+ }
+ max = ee->ee_n_piers[mode] - 1;
+
+ /* Frequency is below our calibrated
+ * range. Use the lowest power curve
+ * we have */
+ if (target < pcinfo[0].freq) {
+ idx_l = idx_r = 0;
+ goto done;
+ }
+
+ /* Frequency is above our calibrated
+ * range. Use the highest power curve
+ * we have */
+ if (target > pcinfo[max].freq) {
+ idx_l = idx_r = max;
+ goto done;
+ }
+
+ /* Frequency is inside our calibrated
+ * channel range. Pick the surrounding
+ * calibration piers so that we can
+ * interpolate */
+ for (i = 0; i <= max; i++) {
+
+ /* Frequency matches one of our calibration
+ * piers, no need to interpolate, just use
+ * that calibration pier */
+ if (pcinfo[i].freq == target) {
+ idx_l = idx_r = i;
+ goto done;
+ }
+
+ /* We found a calibration pier that's above
+ * frequency, use this pier and the previous
+ * one to interpolate */
+ if (target < pcinfo[i].freq) {
+ idx_r = i;
+ idx_l = idx_r - 1;
+ goto done;
+ }
+ }
+
+done:
+ *pcinfo_l = &pcinfo[idx_l];
+ *pcinfo_r = &pcinfo[idx_r];
+}
+
+/*
+ * Get the surrounding per-rate power calibration data
+ * for a given frequency and interpolate between power
+ * values to set max target power supported by hw for
+ * each rate.
+ */
+static void
+ath5k_get_rate_pcal_data(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel,
+ struct ath5k_rate_pcal_info *rates)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_rate_pcal_info *rpinfo;
+ u8 idx_l, idx_r;
+ u8 mode, max, i;
+ u32 target = channel->center_freq;
+
+ idx_l = 0;
+ idx_r = 0;
+
+ switch (channel->hw_value) {
+ case AR5K_MODE_11A:
+ rpinfo = ee->ee_rate_tpwr_a;
+ mode = AR5K_EEPROM_MODE_11A;
+ break;
+ case AR5K_MODE_11B:
+ rpinfo = ee->ee_rate_tpwr_b;
+ mode = AR5K_EEPROM_MODE_11B;
+ break;
+ case AR5K_MODE_11G:
+ default:
+ rpinfo = ee->ee_rate_tpwr_g;
+ mode = AR5K_EEPROM_MODE_11G;
+ break;
+ }
+ max = ee->ee_rate_target_pwr_num[mode] - 1;
+
+ /* Get the surrounding calibration
+ * piers - same as above */
+ if (target < rpinfo[0].freq) {
+ idx_l = idx_r = 0;
+ goto done;
+ }
+
+ if (target > rpinfo[max].freq) {
+ idx_l = idx_r = max;
+ goto done;
+ }
+
+ for (i = 0; i <= max; i++) {
+
+ if (rpinfo[i].freq == target) {
+ idx_l = idx_r = i;
+ goto done;
+ }
+
+ if (target < rpinfo[i].freq) {
+ idx_r = i;
+ idx_l = idx_r - 1;
+ goto done;
+ }
+ }
+
+done:
+ /* Now interpolate power value, based on the frequency */
+ rates->freq = target;
+
+ rates->target_power_6to24 =
+ ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
+ rpinfo[idx_r].freq,
+ rpinfo[idx_l].target_power_6to24,
+ rpinfo[idx_r].target_power_6to24);
+
+ rates->target_power_36 =
+ ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
+ rpinfo[idx_r].freq,
+ rpinfo[idx_l].target_power_36,
+ rpinfo[idx_r].target_power_36);
+
+ rates->target_power_48 =
+ ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
+ rpinfo[idx_r].freq,
+ rpinfo[idx_l].target_power_48,
+ rpinfo[idx_r].target_power_48);
+
+ rates->target_power_54 =
+ ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
+ rpinfo[idx_r].freq,
+ rpinfo[idx_l].target_power_54,
+ rpinfo[idx_r].target_power_54);
+}
+
+/*
+ * Get the max edge power for this channel if
+ * we have such data from EEPROM's Conformance Test
+ * Limits (CTL), and limit max power if needed.
+ */
+static void
+ath5k_get_max_ctl_power(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel)
+{
+ struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_edge_power *rep = ee->ee_ctl_pwr;
+ u8 *ctl_val = ee->ee_ctl;
+ s16 max_chan_pwr = ah->ah_txpower.txp_max_pwr / 4;
+ s16 edge_pwr = 0;
+ u8 rep_idx;
+ u8 i, ctl_mode;
+ u8 ctl_idx = 0xFF;
+ u32 target = channel->center_freq;
+
+ ctl_mode = ath_regd_get_band_ctl(regulatory, channel->band);
+
+ switch (channel->hw_value) {
+ case AR5K_MODE_11A:
+ if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
+ ctl_mode |= AR5K_CTL_TURBO;
+ else
+ ctl_mode |= AR5K_CTL_11A;
+ break;
+ case AR5K_MODE_11G:
+ if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
+ ctl_mode |= AR5K_CTL_TURBOG;
+ else
+ ctl_mode |= AR5K_CTL_11G;
+ break;
+ case AR5K_MODE_11B:
+ ctl_mode |= AR5K_CTL_11B;
+ break;
+ default:
+ return;
+ }
+
+ for (i = 0; i < ee->ee_ctls; i++) {
+ if (ctl_val[i] == ctl_mode) {
+ ctl_idx = i;
+ break;
+ }
+ }
+
+ /* If we have a CTL dataset available grab it and find the
+ * edge power for our frequency */
+ if (ctl_idx == 0xFF)
+ return;
+
+ /* Edge powers are sorted by frequency from lower
+ * to higher. Each CTL corresponds to 8 edge power
+ * measurements. */
+ rep_idx = ctl_idx * AR5K_EEPROM_N_EDGES;
+
+ /* Don't do boundaries check because we
+ * might have more that one bands defined
+ * for this mode */
+
+ /* Get the edge power that's closer to our
+ * frequency */
+ for (i = 0; i < AR5K_EEPROM_N_EDGES; i++) {
+ rep_idx += i;
+ if (target <= rep[rep_idx].freq)
+ edge_pwr = (s16) rep[rep_idx].edge;
+ }
+
+ if (edge_pwr)
+ ah->ah_txpower.txp_max_pwr = 4 * min(edge_pwr, max_chan_pwr);
+}
+
+
+/*
+ * Power to PCDAC table functions
+ */
+
+/*
+ * Fill Power to PCDAC table on RF5111
+ *
+ * No further processing is needed for RF5111, the only thing we have to
+ * do is fill the values below and above calibration range since eeprom data
+ * may not cover the entire PCDAC table.
+ */
+static void
+ath5k_fill_pwr_to_pcdac_table(struct ath5k_hw *ah, s16* table_min,
+ s16 *table_max)
+{
+ u8 *pcdac_out = ah->ah_txpower.txp_pd_table;
+ u8 *pcdac_tmp = ah->ah_txpower.tmpL[0];
+ u8 pcdac_0, pcdac_n, pcdac_i, pwr_idx, i;
+ s16 min_pwr, max_pwr;
+
+ /* Get table boundaries */
+ min_pwr = table_min[0];
+ pcdac_0 = pcdac_tmp[0];
+
+ max_pwr = table_max[0];
+ pcdac_n = pcdac_tmp[table_max[0] - table_min[0]];
+
+ /* Extrapolate below minimum using pcdac_0 */
+ pcdac_i = 0;
+ for (i = 0; i < min_pwr; i++)
+ pcdac_out[pcdac_i++] = pcdac_0;
+
+ /* Copy values from pcdac_tmp */
+ pwr_idx = min_pwr;
+ for (i = 0; pwr_idx <= max_pwr &&
+ pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE; i++) {
+ pcdac_out[pcdac_i++] = pcdac_tmp[i];
+ pwr_idx++;
+ }
+
+ /* Extrapolate above maximum */
+ while (pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE)
+ pcdac_out[pcdac_i++] = pcdac_n;
+
+}
+
+/*
+ * Combine available XPD Curves and fill Linear Power to PCDAC table
+ * on RF5112
+ *
+ * RFX112 can have up to 2 curves (one for low txpower range and one for
+ * higher txpower range). We need to put them both on pcdac_out and place
+ * them in the correct location. In case we only have one curve available
+ * just fit it on pcdac_out (it's supposed to cover the entire range of
+ * available pwr levels since it's always the higher power curve). Extrapolate
+ * below and above final table if needed.
+ */
+static void
+ath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min,
+ s16 *table_max, u8 pdcurves)
+{
+ u8 *pcdac_out = ah->ah_txpower.txp_pd_table;
+ u8 *pcdac_low_pwr;
+ u8 *pcdac_high_pwr;
+ u8 *pcdac_tmp;
+ u8 pwr;
+ s16 max_pwr_idx;
+ s16 min_pwr_idx;
+ s16 mid_pwr_idx = 0;
+ /* Edge flag turns on the 7nth bit on the PCDAC
+ * to declare the higher power curve (force values
+ * to be greater than 64). If we only have one curve
+ * we don't need to set this, if we have 2 curves and
+ * fill the table backwards this can also be used to
+ * switch from higher power curve to lower power curve */
+ u8 edge_flag;
+ int i;
+
+ /* When we have only one curve available
+ * that's the higher power curve. If we have
+ * two curves the first is the high power curve
+ * and the next is the low power curve. */
+ if (pdcurves > 1) {
+ pcdac_low_pwr = ah->ah_txpower.tmpL[1];
+ pcdac_high_pwr = ah->ah_txpower.tmpL[0];
+ mid_pwr_idx = table_max[1] - table_min[1] - 1;
+ max_pwr_idx = (table_max[0] - table_min[0]) / 2;
+
+ /* If table size goes beyond 31.5dB, keep the
+ * upper 31.5dB range when setting tx power.
+ * Note: 126 = 31.5 dB in quarter dB steps */
+ if (table_max[0] - table_min[1] > 126)
+ min_pwr_idx = table_max[0] - 126;
+ else
+ min_pwr_idx = table_min[1];
+
+ /* Since we fill table backwards
+ * start from high power curve */
+ pcdac_tmp = pcdac_high_pwr;
+
+ edge_flag = 0x40;
+ } else {
+ pcdac_low_pwr = ah->ah_txpower.tmpL[1]; /* Zeroed */
+ pcdac_high_pwr = ah->ah_txpower.tmpL[0];
+ min_pwr_idx = table_min[0];
+ max_pwr_idx = (table_max[0] - table_min[0]) / 2;
+ pcdac_tmp = pcdac_high_pwr;
+ edge_flag = 0;
+ }
+
+ /* This is used when setting tx power*/
+ ah->ah_txpower.txp_min_idx = min_pwr_idx / 2;
+
+ /* Fill Power to PCDAC table backwards */
+ pwr = max_pwr_idx;
+ for (i = 63; i >= 0; i--) {
+ /* Entering lower power range, reset
+ * edge flag and set pcdac_tmp to lower
+ * power curve.*/
+ if (edge_flag == 0x40 &&
+ (2 * pwr <= (table_max[1] - table_min[0]) || pwr == 0)) {
+ edge_flag = 0x00;
+ pcdac_tmp = pcdac_low_pwr;
+ pwr = mid_pwr_idx / 2;
+ }
+
+ /* Don't go below 1, extrapolate below if we have
+ * already switched to the lower power curve -or
+ * we only have one curve and edge_flag is zero
+ * anyway */
+ if (pcdac_tmp[pwr] < 1 && (edge_flag == 0x00)) {
+ while (i >= 0) {
+ pcdac_out[i] = pcdac_out[i + 1];
+ i--;
+ }
+ break;
+ }
+
+ pcdac_out[i] = pcdac_tmp[pwr] | edge_flag;
+
+ /* Extrapolate above if pcdac is greater than
+ * 126 -this can happen because we OR pcdac_out
+ * value with edge_flag on high power curve */
+ if (pcdac_out[i] > 126)
+ pcdac_out[i] = 126;
+
+ /* Decrease by a 0.5dB step */
+ pwr--;
+ }
+}
+
+/* Write PCDAC values on hw */
+static void
+ath5k_write_pcdac_table(struct ath5k_hw *ah)
+{
+ u8 *pcdac_out = ah->ah_txpower.txp_pd_table;
+ int i;
+
+ /*
+ * Write TX power values
+ */
+ for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
+ ath5k_hw_reg_write(ah,
+ (((pcdac_out[2 * i + 0] << 8 | 0xff) & 0xffff) << 0) |
+ (((pcdac_out[2 * i + 1] << 8 | 0xff) & 0xffff) << 16),
+ AR5K_PHY_PCDAC_TXPOWER(i));
+ }
+}
+
+
+/*
+ * Power to PDADC table functions
+ */
+
+/*
+ * Set the gain boundaries and create final Power to PDADC table
+ *
+ * We can have up to 4 pd curves, we need to do a similar process
+ * as we do for RF5112. This time we don't have an edge_flag but we
+ * set the gain boundaries on a separate register.
+ */
+static void
+ath5k_combine_pwr_to_pdadc_curves(struct ath5k_hw *ah,
+ s16 *pwr_min, s16 *pwr_max, u8 pdcurves)
+{
+ u8 gain_boundaries[AR5K_EEPROM_N_PD_GAINS];
+ u8 *pdadc_out = ah->ah_txpower.txp_pd_table;
+ u8 *pdadc_tmp;
+ s16 pdadc_0;
+ u8 pdadc_i, pdadc_n, pwr_step, pdg, max_idx, table_size;
+ u8 pd_gain_overlap;
+
+ /* Note: Register value is initialized on initvals
+ * there is no feedback from hw.
+ * XXX: What about pd_gain_overlap from EEPROM ? */
+ pd_gain_overlap = (u8) ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG5) &
+ AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP;
+
+ /* Create final PDADC table */
+ for (pdg = 0, pdadc_i = 0; pdg < pdcurves; pdg++) {
+ pdadc_tmp = ah->ah_txpower.tmpL[pdg];
+
+ if (pdg == pdcurves - 1)
+ /* 2 dB boundary stretch for last
+ * (higher power) curve */
+ gain_boundaries[pdg] = pwr_max[pdg] + 4;
+ else
+ /* Set gain boundary in the middle
+ * between this curve and the next one */
+ gain_boundaries[pdg] =
+ (pwr_max[pdg] + pwr_min[pdg + 1]) / 2;
+
+ /* Sanity check in case our 2 db stretch got out of
+ * range. */
+ if (gain_boundaries[pdg] > AR5K_TUNE_MAX_TXPOWER)
+ gain_boundaries[pdg] = AR5K_TUNE_MAX_TXPOWER;
+
+ /* For the first curve (lower power)
+ * start from 0 dB */
+ if (pdg == 0)
+ pdadc_0 = 0;
+ else
+ /* For the other curves use the gain overlap */
+ pdadc_0 = (gain_boundaries[pdg - 1] - pwr_min[pdg]) -
+ pd_gain_overlap;
+
+ /* Force each power step to be at least 0.5 dB */
+ if ((pdadc_tmp[1] - pdadc_tmp[0]) > 1)
+ pwr_step = pdadc_tmp[1] - pdadc_tmp[0];
+ else
+ pwr_step = 1;
+
+ /* If pdadc_0 is negative, we need to extrapolate
+ * below this pdgain by a number of pwr_steps */
+ while ((pdadc_0 < 0) && (pdadc_i < 128)) {
+ s16 tmp = pdadc_tmp[0] + pdadc_0 * pwr_step;
+ pdadc_out[pdadc_i++] = (tmp < 0) ? 0 : (u8) tmp;
+ pdadc_0++;
+ }
+
+ /* Set last pwr level, using gain boundaries */
+ pdadc_n = gain_boundaries[pdg] + pd_gain_overlap - pwr_min[pdg];
+ /* Limit it to be inside pwr range */
+ table_size = pwr_max[pdg] - pwr_min[pdg];
+ max_idx = (pdadc_n < table_size) ? pdadc_n : table_size;
+
+ /* Fill pdadc_out table */
+ while (pdadc_0 < max_idx && pdadc_i < 128)
+ pdadc_out[pdadc_i++] = pdadc_tmp[pdadc_0++];
+
+ /* Need to extrapolate above this pdgain? */
+ if (pdadc_n <= max_idx)
+ continue;
+
+ /* Force each power step to be at least 0.5 dB */
+ if ((pdadc_tmp[table_size - 1] - pdadc_tmp[table_size - 2]) > 1)
+ pwr_step = pdadc_tmp[table_size - 1] -
+ pdadc_tmp[table_size - 2];
+ else
+ pwr_step = 1;
+
+ /* Extrapolate above */
+ while ((pdadc_0 < (s16) pdadc_n) &&
+ (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2)) {
+ s16 tmp = pdadc_tmp[table_size - 1] +
+ (pdadc_0 - max_idx) * pwr_step;
+ pdadc_out[pdadc_i++] = (tmp > 127) ? 127 : (u8) tmp;
+ pdadc_0++;
+ }
+ }
+
+ while (pdg < AR5K_EEPROM_N_PD_GAINS) {
+ gain_boundaries[pdg] = gain_boundaries[pdg - 1];
+ pdg++;
+ }
+
+ while (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2) {
+ pdadc_out[pdadc_i] = pdadc_out[pdadc_i - 1];
+ pdadc_i++;
+ }
+
+ /* Set gain boundaries */
+ ath5k_hw_reg_write(ah,
+ AR5K_REG_SM(pd_gain_overlap,
+ AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP) |
+ AR5K_REG_SM(gain_boundaries[0],
+ AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1) |
+ AR5K_REG_SM(gain_boundaries[1],
+ AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2) |
+ AR5K_REG_SM(gain_boundaries[2],
+ AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3) |
+ AR5K_REG_SM(gain_boundaries[3],
+ AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4),
+ AR5K_PHY_TPC_RG5);
+
+ /* Used for setting rate power table */
+ ah->ah_txpower.txp_min_idx = pwr_min[0];
+
+}
+
+/* Write PDADC values on hw */
+static void
+ath5k_write_pwr_to_pdadc_table(struct ath5k_hw *ah, u8 ee_mode)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ u8 *pdadc_out = ah->ah_txpower.txp_pd_table;
+ u8 *pdg_to_idx = ee->ee_pdc_to_idx[ee_mode];
+ u8 pdcurves = ee->ee_pd_gains[ee_mode];
+ u32 reg;
+ u8 i;
+
+ /* Select the right pdgain curves */
+
+ /* Clear current settings */
+ reg = ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG1);
+ reg &= ~(AR5K_PHY_TPC_RG1_PDGAIN_1 |
+ AR5K_PHY_TPC_RG1_PDGAIN_2 |
+ AR5K_PHY_TPC_RG1_PDGAIN_3 |
+ AR5K_PHY_TPC_RG1_NUM_PD_GAIN);
+
+ /*
+ * Use pd_gains curve from eeprom
+ *
+ * This overrides the default setting from initvals
+ * in case some vendors (e.g. Zcomax) don't use the default
+ * curves. If we don't honor their settings we 'll get a
+ * 5dB (1 * gain overlap ?) drop.
+ */
+ reg |= AR5K_REG_SM(pdcurves, AR5K_PHY_TPC_RG1_NUM_PD_GAIN);
+
+ switch (pdcurves) {
+ case 3:
+ reg |= AR5K_REG_SM(pdg_to_idx[2], AR5K_PHY_TPC_RG1_PDGAIN_3);
+ /* Fall through */
+ case 2:
+ reg |= AR5K_REG_SM(pdg_to_idx[1], AR5K_PHY_TPC_RG1_PDGAIN_2);
+ /* Fall through */
+ case 1:
+ reg |= AR5K_REG_SM(pdg_to_idx[0], AR5K_PHY_TPC_RG1_PDGAIN_1);
+ break;
+ }
+ ath5k_hw_reg_write(ah, reg, AR5K_PHY_TPC_RG1);
+
+ /*
+ * Write TX power values
+ */
+ for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
+ u32 val = get_unaligned_le32(&pdadc_out[4 * i]);
+ ath5k_hw_reg_write(ah, val, AR5K_PHY_PDADC_TXPOWER(i));
+ }
+}
+
+
+/*
+ * Common code for PCDAC/PDADC tables
+ */
+
+/*
+ * This is the main function that uses all of the above
+ * to set PCDAC/PDADC table on hw for the current channel.
+ * This table is used for tx power calibration on the baseband,
+ * without it we get weird tx power levels and in some cases
+ * distorted spectral mask
+ */
+static int
+ath5k_setup_channel_powertable(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel,
+ u8 ee_mode, u8 type)
+{
+ struct ath5k_pdgain_info *pdg_L, *pdg_R;
+ struct ath5k_chan_pcal_info *pcinfo_L;
+ struct ath5k_chan_pcal_info *pcinfo_R;
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ u8 *pdg_curve_to_idx = ee->ee_pdc_to_idx[ee_mode];
+ s16 table_min[AR5K_EEPROM_N_PD_GAINS];
+ s16 table_max[AR5K_EEPROM_N_PD_GAINS];
+ u8 *tmpL;
+ u8 *tmpR;
+ u32 target = channel->center_freq;
+ int pdg, i;
+
+ /* Get surrounding freq piers for this channel */
+ ath5k_get_chan_pcal_surrounding_piers(ah, channel,
+ &pcinfo_L,
+ &pcinfo_R);
+
+ /* Loop over pd gain curves on
+ * surrounding freq piers by index */
+ for (pdg = 0; pdg < ee->ee_pd_gains[ee_mode]; pdg++) {
+
+ /* Fill curves in reverse order
+ * from lower power (max gain)
+ * to higher power. Use curve -> idx
+ * backmapping we did on eeprom init */
+ u8 idx = pdg_curve_to_idx[pdg];
+
+ /* Grab the needed curves by index */
+ pdg_L = &pcinfo_L->pd_curves[idx];
+ pdg_R = &pcinfo_R->pd_curves[idx];
+
+ /* Initialize the temp tables */
+ tmpL = ah->ah_txpower.tmpL[pdg];
+ tmpR = ah->ah_txpower.tmpR[pdg];
+
+ /* Set curve's x boundaries and create
+ * curves so that they cover the same
+ * range (if we don't do that one table
+ * will have values on some range and the
+ * other one won't have any so interpolation
+ * will fail) */
+ table_min[pdg] = min(pdg_L->pd_pwr[0],
+ pdg_R->pd_pwr[0]) / 2;
+
+ table_max[pdg] = max(pdg_L->pd_pwr[pdg_L->pd_points - 1],
+ pdg_R->pd_pwr[pdg_R->pd_points - 1]) / 2;
+
+ /* Now create the curves on surrounding channels
+ * and interpolate if needed to get the final
+ * curve for this gain on this channel */
+ switch (type) {
+ case AR5K_PWRTABLE_LINEAR_PCDAC:
+ /* Override min/max so that we don't loose
+ * accuracy (don't divide by 2) */
+ table_min[pdg] = min(pdg_L->pd_pwr[0],
+ pdg_R->pd_pwr[0]);
+
+ table_max[pdg] =
+ max(pdg_L->pd_pwr[pdg_L->pd_points - 1],
+ pdg_R->pd_pwr[pdg_R->pd_points - 1]);
+
+ /* Override minimum so that we don't get
+ * out of bounds while extrapolating
+ * below. Don't do this when we have 2
+ * curves and we are on the high power curve
+ * because table_min is ok in this case */
+ if (!(ee->ee_pd_gains[ee_mode] > 1 && pdg == 0)) {
+
+ table_min[pdg] =
+ ath5k_get_linear_pcdac_min(pdg_L->pd_step,
+ pdg_R->pd_step,
+ pdg_L->pd_pwr,
+ pdg_R->pd_pwr);
+
+ /* Don't go too low because we will
+ * miss the upper part of the curve.
+ * Note: 126 = 31.5dB (max power supported)
+ * in 0.25dB units */
+ if (table_max[pdg] - table_min[pdg] > 126)
+ table_min[pdg] = table_max[pdg] - 126;
+ }
+
+ /* Fall through */
+ case AR5K_PWRTABLE_PWR_TO_PCDAC:
+ case AR5K_PWRTABLE_PWR_TO_PDADC:
+
+ ath5k_create_power_curve(table_min[pdg],
+ table_max[pdg],
+ pdg_L->pd_pwr,
+ pdg_L->pd_step,
+ pdg_L->pd_points, tmpL, type);
+
+ /* We are in a calibration
+ * pier, no need to interpolate
+ * between freq piers */
+ if (pcinfo_L == pcinfo_R)
+ continue;
+
+ ath5k_create_power_curve(table_min[pdg],
+ table_max[pdg],
+ pdg_R->pd_pwr,
+ pdg_R->pd_step,
+ pdg_R->pd_points, tmpR, type);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Interpolate between curves
+ * of surrounding freq piers to
+ * get the final curve for this
+ * pd gain. Re-use tmpL for interpolation
+ * output */
+ for (i = 0; (i < (u16) (table_max[pdg] - table_min[pdg])) &&
+ (i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) {
+ tmpL[i] = (u8) ath5k_get_interpolated_value(target,
+ (s16) pcinfo_L->freq,
+ (s16) pcinfo_R->freq,
+ (s16) tmpL[i],
+ (s16) tmpR[i]);
+ }
+ }
+
+ /* Now we have a set of curves for this
+ * channel on tmpL (x range is table_max - table_min
+ * and y values are tmpL[pdg][]) sorted in the same
+ * order as EEPROM (because we've used the backmapping).
+ * So for RF5112 it's from higher power to lower power
+ * and for RF2413 it's from lower power to higher power.
+ * For RF5111 we only have one curve. */
+
+ /* Fill min and max power levels for this
+ * channel by interpolating the values on
+ * surrounding channels to complete the dataset */
+ ah->ah_txpower.txp_min_pwr = ath5k_get_interpolated_value(target,
+ (s16) pcinfo_L->freq,
+ (s16) pcinfo_R->freq,
+ pcinfo_L->min_pwr, pcinfo_R->min_pwr);
+
+ ah->ah_txpower.txp_max_pwr = ath5k_get_interpolated_value(target,
+ (s16) pcinfo_L->freq,
+ (s16) pcinfo_R->freq,
+ pcinfo_L->max_pwr, pcinfo_R->max_pwr);
+
+ /* Fill PCDAC/PDADC table */
+ switch (type) {
+ case AR5K_PWRTABLE_LINEAR_PCDAC:
+ /* For RF5112 we can have one or two curves
+ * and each curve covers a certain power lvl
+ * range so we need to do some more processing */
+ ath5k_combine_linear_pcdac_curves(ah, table_min, table_max,
+ ee->ee_pd_gains[ee_mode]);
+
+ /* Set txp.offset so that we can
+ * match max power value with max
+ * table index */
+ ah->ah_txpower.txp_offset = 64 - (table_max[0] / 2);
+ break;
+ case AR5K_PWRTABLE_PWR_TO_PCDAC:
+ /* We are done for RF5111 since it has only
+ * one curve, just fit the curve on the table */
+ ath5k_fill_pwr_to_pcdac_table(ah, table_min, table_max);
+
+ /* No rate powertable adjustment for RF5111 */
+ ah->ah_txpower.txp_min_idx = 0;
+ ah->ah_txpower.txp_offset = 0;
+ break;
+ case AR5K_PWRTABLE_PWR_TO_PDADC:
+ /* Set PDADC boundaries and fill
+ * final PDADC table */
+ ath5k_combine_pwr_to_pdadc_curves(ah, table_min, table_max,
+ ee->ee_pd_gains[ee_mode]);
+
+ /* Set txp.offset, note that table_min
+ * can be negative */
+ ah->ah_txpower.txp_offset = table_min[0];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ah->ah_txpower.txp_setup = true;
+
+ return 0;
+}
+
+/* Write power table for current channel to hw */
+static void
+ath5k_write_channel_powertable(struct ath5k_hw *ah, u8 ee_mode, u8 type)
+{
+ if (type == AR5K_PWRTABLE_PWR_TO_PDADC)
+ ath5k_write_pwr_to_pdadc_table(ah, ee_mode);
+ else
+ ath5k_write_pcdac_table(ah);
+}
+
+/*
+ * Per-rate tx power setting
+ *
+ * This is the code that sets the desired tx power (below
+ * maximum) on hw for each rate (we also have TPC that sets
+ * power per packet). We do that by providing an index on the
+ * PCDAC/PDADC table we set up.
+ */
+
+/*
+ * Set rate power table
+ *
+ * For now we only limit txpower based on maximum tx power
+ * supported by hw (what's inside rate_info). We need to limit
+ * this even more, based on regulatory domain etc.
+ *
+ * Rate power table contains indices to PCDAC/PDADC table (0.5dB steps)
+ * and is indexed as follows:
+ * rates[0] - rates[7] -> OFDM rates
+ * rates[8] - rates[14] -> CCK rates
+ * rates[15] -> XR rates (they all have the same power)
+ */
+static void
+ath5k_setup_rate_powertable(struct ath5k_hw *ah, u16 max_pwr,
+ struct ath5k_rate_pcal_info *rate_info,
+ u8 ee_mode)
+{
+ unsigned int i;
+ u16 *rates;
+
+ /* max_pwr is power level we got from driver/user in 0.5dB
+ * units, switch to 0.25dB units so we can compare */
+ max_pwr *= 2;
+ max_pwr = min(max_pwr, (u16) ah->ah_txpower.txp_max_pwr) / 2;
+
+ /* apply rate limits */
+ rates = ah->ah_txpower.txp_rates_power_table;
+
+ /* OFDM rates 6 to 24Mb/s */
+ for (i = 0; i < 5; i++)
+ rates[i] = min(max_pwr, rate_info->target_power_6to24);
+
+ /* Rest OFDM rates */
+ rates[5] = min(rates[0], rate_info->target_power_36);
+ rates[6] = min(rates[0], rate_info->target_power_48);
+ rates[7] = min(rates[0], rate_info->target_power_54);
+
+ /* CCK rates */
+ /* 1L */
+ rates[8] = min(rates[0], rate_info->target_power_6to24);
+ /* 2L */
+ rates[9] = min(rates[0], rate_info->target_power_36);
+ /* 2S */
+ rates[10] = min(rates[0], rate_info->target_power_36);
+ /* 5L */
+ rates[11] = min(rates[0], rate_info->target_power_48);
+ /* 5S */
+ rates[12] = min(rates[0], rate_info->target_power_48);
+ /* 11L */
+ rates[13] = min(rates[0], rate_info->target_power_54);
+ /* 11S */
+ rates[14] = min(rates[0], rate_info->target_power_54);
+
+ /* XR rates */
+ rates[15] = min(rates[0], rate_info->target_power_6to24);
+
+ /* CCK rates have different peak to average ratio
+ * so we have to tweak their power so that gainf
+ * correction works ok. For this we use OFDM to
+ * CCK delta from eeprom */
+ if ((ee_mode == AR5K_EEPROM_MODE_11G) &&
+ (ah->ah_phy_revision < AR5K_SREV_PHY_5212A))
+ for (i = 8; i <= 15; i++)
+ rates[i] -= ah->ah_txpower.txp_cck_ofdm_gainf_delta;
+
+ /* Now that we have all rates setup use table offset to
+ * match the power range set by user with the power indices
+ * on PCDAC/PDADC table */
+ for (i = 0; i < 16; i++) {
+ rates[i] += ah->ah_txpower.txp_offset;
+ /* Don't get out of bounds */
+ if (rates[i] > 63)
+ rates[i] = 63;
+ }
+
+ /* Min/max in 0.25dB units */
+ ah->ah_txpower.txp_min_pwr = 2 * rates[7];
+ ah->ah_txpower.txp_cur_pwr = 2 * rates[0];
+ ah->ah_txpower.txp_ofdm = rates[7];
+}
+
+
+/*
+ * Set transmission power
+ */
+static int
+ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
+ u8 txpower)
+{
+ struct ath5k_rate_pcal_info rate_info;
+ struct ieee80211_channel *curr_channel = ah->ah_current_channel;
+ int ee_mode;
+ u8 type;
+ int ret;
+
+ if (txpower > AR5K_TUNE_MAX_TXPOWER) {
+ ATH5K_ERR(ah, "invalid tx power: %u\n", txpower);
+ return -EINVAL;
+ }
+
+ ee_mode = ath5k_eeprom_mode_from_channel(channel);
+ if (ee_mode < 0) {
+ ATH5K_ERR(ah,
+ "invalid channel: %d\n", channel->center_freq);
+ return -EINVAL;
+ }
+
+ /* Initialize TX power table */
+ switch (ah->ah_radio) {
+ case AR5K_RF5110:
+ /* TODO */
+ return 0;
+ case AR5K_RF5111:
+ type = AR5K_PWRTABLE_PWR_TO_PCDAC;
+ break;
+ case AR5K_RF5112:
+ type = AR5K_PWRTABLE_LINEAR_PCDAC;
+ break;
+ case AR5K_RF2413:
+ case AR5K_RF5413:
+ case AR5K_RF2316:
+ case AR5K_RF2317:
+ case AR5K_RF2425:
+ type = AR5K_PWRTABLE_PWR_TO_PDADC;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * If we don't change channel/mode skip tx powertable calculation
+ * and use the cached one.
+ */
+ if (!ah->ah_txpower.txp_setup ||
+ (channel->hw_value != curr_channel->hw_value) ||
+ (channel->center_freq != curr_channel->center_freq)) {
+ /* Reset TX power values */
+ memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
+ ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
+
+ /* Calculate the powertable */
+ ret = ath5k_setup_channel_powertable(ah, channel,
+ ee_mode, type);
+ if (ret)
+ return ret;
+ }
+
+ /* Write table on hw */
+ ath5k_write_channel_powertable(ah, ee_mode, type);
+
+ /* Limit max power if we have a CTL available */
+ ath5k_get_max_ctl_power(ah, channel);
+
+ /* FIXME: Antenna reduction stuff */
+
+ /* FIXME: Limit power on turbo modes */
+
+ /* FIXME: TPC scale reduction */
+
+ /* Get surrounding channels for per-rate power table
+ * calibration */
+ ath5k_get_rate_pcal_data(ah, channel, &rate_info);
+
+ /* Setup rate power table */
+ ath5k_setup_rate_powertable(ah, txpower, &rate_info, ee_mode);
+
+ /* Write rate power table on hw */
+ ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(3, 24) |
+ AR5K_TXPOWER_OFDM(2, 16) | AR5K_TXPOWER_OFDM(1, 8) |
+ AR5K_TXPOWER_OFDM(0, 0), AR5K_PHY_TXPOWER_RATE1);
+
+ ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(7, 24) |
+ AR5K_TXPOWER_OFDM(6, 16) | AR5K_TXPOWER_OFDM(5, 8) |
+ AR5K_TXPOWER_OFDM(4, 0), AR5K_PHY_TXPOWER_RATE2);
+
+ ath5k_hw_reg_write(ah, AR5K_TXPOWER_CCK(10, 24) |
+ AR5K_TXPOWER_CCK(9, 16) | AR5K_TXPOWER_CCK(15, 8) |
+ AR5K_TXPOWER_CCK(8, 0), AR5K_PHY_TXPOWER_RATE3);
+
+ ath5k_hw_reg_write(ah, AR5K_TXPOWER_CCK(14, 24) |
+ AR5K_TXPOWER_CCK(13, 16) | AR5K_TXPOWER_CCK(12, 8) |
+ AR5K_TXPOWER_CCK(11, 0), AR5K_PHY_TXPOWER_RATE4);
+
+ /* FIXME: TPC support */
+ if (ah->ah_txpower.txp_tpc) {
+ ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE |
+ AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
+
+ ath5k_hw_reg_write(ah,
+ AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_ACK) |
+ AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CTS) |
+ AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP),
+ AR5K_TPC);
+ } else {
+ ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX |
+ AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
+ }
+
+ return 0;
+}
+
+int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower)
+{
+ ATH5K_DBG(ah, ATH5K_DEBUG_TXPOWER,
+ "changing txpower to %d\n", txpower);
+
+ return ath5k_hw_txpower(ah, ah->ah_current_channel, txpower);
+}
+
+/*************\
+ Init function
+\*************/
+
+int ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
+ u8 mode, bool fast)
+{
+ struct ieee80211_channel *curr_channel;
+ int ret, i;
+ u32 phy_tst1;
+ ret = 0;
+
+ /*
+ * Sanity check for fast flag
+ * Don't try fast channel change when changing modulation
+ * mode/band. We check for chip compatibility on
+ * ath5k_hw_reset.
+ */
+ curr_channel = ah->ah_current_channel;
+ if (fast && (channel->hw_value != curr_channel->hw_value))
+ return -EINVAL;
+
+ /*
+ * On fast channel change we only set the synth parameters
+ * while PHY is running, enable calibration and skip the rest.
+ */
+ if (fast) {
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_RFBUS_REQ,
+ AR5K_PHY_RFBUS_REQ_REQUEST);
+ for (i = 0; i < 100; i++) {
+ if (ath5k_hw_reg_read(ah, AR5K_PHY_RFBUS_GRANT))
+ break;
+ udelay(5);
+ }
+ /* Failed */
+ if (i >= 100)
+ return -EIO;
+
+ /* Set channel and wait for synth */
+ ret = ath5k_hw_channel(ah, channel);
+ if (ret)
+ return ret;
+
+ ath5k_hw_wait_for_synth(ah, channel);
+ }
+
+ /*
+ * Set TX power
+ *
+ * Note: We need to do that before we set
+ * RF buffer settings on 5211/5212+ so that we
+ * properly set curve indices.
+ */
+ ret = ath5k_hw_txpower(ah, channel, ah->ah_txpower.txp_cur_pwr ?
+ ah->ah_txpower.txp_cur_pwr / 2 : AR5K_TUNE_MAX_TXPOWER);
+ if (ret)
+ return ret;
+
+ /* Write OFDM timings on 5212*/
+ if (ah->ah_version == AR5K_AR5212 &&
+ channel->hw_value != AR5K_MODE_11B) {
+
+ ret = ath5k_hw_write_ofdm_timings(ah, channel);
+ if (ret)
+ return ret;
+
+ /* Spur info is available only from EEPROM versions
+ * greater than 5.3, but the EEPROM routines will use
+ * static values for older versions */
+ if (ah->ah_mac_srev >= AR5K_SREV_AR5424)
+ ath5k_hw_set_spur_mitigation_filter(ah,
+ channel);
+ }
+
+ /* If we used fast channel switching
+ * we are done, release RF bus and
+ * fire up NF calibration.
+ *
+ * Note: Only NF calibration due to
+ * channel change, not AGC calibration
+ * since AGC is still running !
+ */
+ if (fast) {
+ /*
+ * Release RF Bus grant
+ */
+ AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_RFBUS_REQ,
+ AR5K_PHY_RFBUS_REQ_REQUEST);
+
+ /*
+ * Start NF calibration
+ */
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
+ AR5K_PHY_AGCCTL_NF);
+
+ return ret;
+ }
+
+ /*
+ * For 5210 we do all initialization using
+ * initvals, so we don't have to modify
+ * any settings (5210 also only supports
+ * a/aturbo modes)
+ */
+ if (ah->ah_version != AR5K_AR5210) {
+
+ /*
+ * Write initial RF gain settings
+ * This should work for both 5111/5112
+ */
+ ret = ath5k_hw_rfgain_init(ah, channel->band);
+ if (ret)
+ return ret;
+
+ mdelay(1);
+
+ /*
+ * Write RF buffer
+ */
+ ret = ath5k_hw_rfregs_init(ah, channel, mode);
+ if (ret)
+ return ret;
+
+ /*Enable/disable 802.11b mode on 5111
+ (enable 2111 frequency converter + CCK)*/
+ if (ah->ah_radio == AR5K_RF5111) {
+ if (mode == AR5K_MODE_11B)
+ AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG,
+ AR5K_TXCFG_B_MODE);
+ else
+ AR5K_REG_DISABLE_BITS(ah, AR5K_TXCFG,
+ AR5K_TXCFG_B_MODE);
+ }
+
+ } else if (ah->ah_version == AR5K_AR5210) {
+ mdelay(1);
+ /* Disable phy and wait */
+ ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT);
+ mdelay(1);
+ }
+
+ /* Set channel on PHY */
+ ret = ath5k_hw_channel(ah, channel);
+ if (ret)
+ return ret;
+
+ /*
+ * Enable the PHY and wait until completion
+ * This includes BaseBand and Synthesizer
+ * activation.
+ */
+ ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT);
+
+ ath5k_hw_wait_for_synth(ah, channel);
+
+ /*
+ * Perform ADC test to see if baseband is ready
+ * Set tx hold and check adc test register
+ */
+ phy_tst1 = ath5k_hw_reg_read(ah, AR5K_PHY_TST1);
+ ath5k_hw_reg_write(ah, AR5K_PHY_TST1_TXHOLD, AR5K_PHY_TST1);
+ for (i = 0; i <= 20; i++) {
+ if (!(ath5k_hw_reg_read(ah, AR5K_PHY_ADC_TEST) & 0x10))
+ break;
+ udelay(200);
+ }
+ ath5k_hw_reg_write(ah, phy_tst1, AR5K_PHY_TST1);
+
+ /*
+ * Start automatic gain control calibration
+ *
+ * During AGC calibration RX path is re-routed to
+ * a power detector so we don't receive anything.
+ *
+ * This method is used to calibrate some static offsets
+ * used together with on-the fly I/Q calibration (the
+ * one performed via ath5k_hw_phy_calibrate), which doesn't
+ * interrupt rx path.
+ *
+ * While rx path is re-routed to the power detector we also
+ * start a noise floor calibration to measure the
+ * card's noise floor (the noise we measure when we are not
+ * transmitting or receiving anything).
+ *
+ * If we are in a noisy environment, AGC calibration may time
+ * out and/or noise floor calibration might timeout.
+ */
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
+ AR5K_PHY_AGCCTL_CAL | AR5K_PHY_AGCCTL_NF);
+
+ /* At the same time start I/Q calibration for QAM constellation
+ * -no need for CCK- */
+ ah->ah_calibration = false;
+ if (!(mode == AR5K_MODE_11B)) {
+ ah->ah_calibration = true;
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ,
+ AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15);
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ,
+ AR5K_PHY_IQ_RUN);
+ }
+
+ /* Wait for gain calibration to finish (we check for I/Q calibration
+ * during ath5k_phy_calibrate) */
+ if (ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL,
+ AR5K_PHY_AGCCTL_CAL, 0, false)) {
+ ATH5K_ERR(ah, "gain calibration timeout (%uMHz)\n",
+ channel->center_freq);
+ }
+
+ /* Restore antenna mode */
+ ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode);
+
+ return ret;
+}
diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c
new file mode 100644
index 0000000..7766542
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/qcu.c
@@ -0,0 +1,641 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ */
+
+/********************************************\
+Queue Control Unit, DFS Control Unit Functions
+\********************************************/
+
+#include "ath5k.h"
+#include "reg.h"
+#include "debug.h"
+
+
+/******************\
+* Helper functions *
+\******************/
+
+/*
+ * Get number of pending frames
+ * for a specific queue [5211+]
+ */
+u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue)
+{
+ u32 pending;
+ AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
+
+ /* Return if queue is declared inactive */
+ if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
+ return false;
+
+ /* XXX: How about AR5K_CFG_TXCNT ? */
+ if (ah->ah_version == AR5K_AR5210)
+ return false;
+
+ pending = ath5k_hw_reg_read(ah, AR5K_QUEUE_STATUS(queue));
+ pending &= AR5K_QCU_STS_FRMPENDCNT;
+
+ /* It's possible to have no frames pending even if TXE
+ * is set. To indicate that q has not stopped return
+ * true */
+ if (!pending && AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue))
+ return true;
+
+ return pending;
+}
+
+/*
+ * Set a transmit queue inactive
+ */
+void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue)
+{
+ if (WARN_ON(queue >= ah->ah_capabilities.cap_queues.q_tx_num))
+ return;
+
+ /* This queue will be skipped in further operations */
+ ah->ah_txq[queue].tqi_type = AR5K_TX_QUEUE_INACTIVE;
+ /*For SIMR setup*/
+ AR5K_Q_DISABLE_BITS(ah->ah_txq_status, queue);
+}
+
+/*
+ * Make sure cw is a power of 2 minus 1 and smaller than 1024
+ */
+static u16 ath5k_cw_validate(u16 cw_req)
+{
+ u32 cw = 1;
+ cw_req = min(cw_req, (u16)1023);
+
+ while (cw < cw_req)
+ cw = (cw << 1) | 1;
+
+ return cw;
+}
+
+/*
+ * Get properties for a transmit queue
+ */
+int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue,
+ struct ath5k_txq_info *queue_info)
+{
+ memcpy(queue_info, &ah->ah_txq[queue], sizeof(struct ath5k_txq_info));
+ return 0;
+}
+
+/*
+ * Set properties for a transmit queue
+ */
+int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue,
+ const struct ath5k_txq_info *qinfo)
+{
+ struct ath5k_txq_info *qi;
+
+ AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
+
+ qi = &ah->ah_txq[queue];
+
+ if (qi->tqi_type == AR5K_TX_QUEUE_INACTIVE)
+ return -EIO;
+
+ /* copy and validate values */
+ qi->tqi_type = qinfo->tqi_type;
+ qi->tqi_subtype = qinfo->tqi_subtype;
+ qi->tqi_flags = qinfo->tqi_flags;
+ /*
+ * According to the docs: Although the AIFS field is 8 bit wide,
+ * the maximum supported value is 0xFC. Setting it higher than that
+ * will cause the DCU to hang.
+ */
+ qi->tqi_aifs = min(qinfo->tqi_aifs, (u8)0xFC);
+ qi->tqi_cw_min = ath5k_cw_validate(qinfo->tqi_cw_min);
+ qi->tqi_cw_max = ath5k_cw_validate(qinfo->tqi_cw_max);
+ qi->tqi_cbr_period = qinfo->tqi_cbr_period;
+ qi->tqi_cbr_overflow_limit = qinfo->tqi_cbr_overflow_limit;
+ qi->tqi_burst_time = qinfo->tqi_burst_time;
+ qi->tqi_ready_time = qinfo->tqi_ready_time;
+
+ /*XXX: Is this supported on 5210 ?*/
+ /*XXX: Is this correct for AR5K_WME_AC_VI,VO ???*/
+ if ((qinfo->tqi_type == AR5K_TX_QUEUE_DATA &&
+ ((qinfo->tqi_subtype == AR5K_WME_AC_VI) ||
+ (qinfo->tqi_subtype == AR5K_WME_AC_VO))) ||
+ qinfo->tqi_type == AR5K_TX_QUEUE_UAPSD)
+ qi->tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS;
+
+ return 0;
+}
+
+/*
+ * Initialize a transmit queue
+ */
+int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type,
+ struct ath5k_txq_info *queue_info)
+{
+ unsigned int queue;
+ int ret;
+
+ /*
+ * Get queue by type
+ */
+ /* 5210 only has 2 queues */
+ if (ah->ah_capabilities.cap_queues.q_tx_num == 2) {
+ switch (queue_type) {
+ case AR5K_TX_QUEUE_DATA:
+ queue = AR5K_TX_QUEUE_ID_NOQCU_DATA;
+ break;
+ case AR5K_TX_QUEUE_BEACON:
+ case AR5K_TX_QUEUE_CAB:
+ queue = AR5K_TX_QUEUE_ID_NOQCU_BEACON;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ switch (queue_type) {
+ case AR5K_TX_QUEUE_DATA:
+ for (queue = AR5K_TX_QUEUE_ID_DATA_MIN;
+ ah->ah_txq[queue].tqi_type !=
+ AR5K_TX_QUEUE_INACTIVE; queue++) {
+
+ if (queue > AR5K_TX_QUEUE_ID_DATA_MAX)
+ return -EINVAL;
+ }
+ break;
+ case AR5K_TX_QUEUE_UAPSD:
+ queue = AR5K_TX_QUEUE_ID_UAPSD;
+ break;
+ case AR5K_TX_QUEUE_BEACON:
+ queue = AR5K_TX_QUEUE_ID_BEACON;
+ break;
+ case AR5K_TX_QUEUE_CAB:
+ queue = AR5K_TX_QUEUE_ID_CAB;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * Setup internal queue structure
+ */
+ memset(&ah->ah_txq[queue], 0, sizeof(struct ath5k_txq_info));
+ ah->ah_txq[queue].tqi_type = queue_type;
+
+ if (queue_info != NULL) {
+ queue_info->tqi_type = queue_type;
+ ret = ath5k_hw_set_tx_queueprops(ah, queue, queue_info);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * We use ah_txq_status to hold a temp value for
+ * the Secondary interrupt mask registers on 5211+
+ * check out ath5k_hw_reset_tx_queue
+ */
+ AR5K_Q_ENABLE_BITS(ah->ah_txq_status, queue);
+
+ return queue;
+}
+
+
+/*******************************\
+* Single QCU/DCU initialization *
+\*******************************/
+
+/*
+ * Set tx retry limits on DCU
+ */
+void ath5k_hw_set_tx_retry_limits(struct ath5k_hw *ah,
+ unsigned int queue)
+{
+ /* Single data queue on AR5210 */
+ if (ah->ah_version == AR5K_AR5210) {
+ struct ath5k_txq_info *tq = &ah->ah_txq[queue];
+
+ if (queue > 0)
+ return;
+
+ ath5k_hw_reg_write(ah,
+ (tq->tqi_cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S)
+ | AR5K_REG_SM(ah->ah_retry_long,
+ AR5K_NODCU_RETRY_LMT_SLG_RETRY)
+ | AR5K_REG_SM(ah->ah_retry_short,
+ AR5K_NODCU_RETRY_LMT_SSH_RETRY)
+ | AR5K_REG_SM(ah->ah_retry_long,
+ AR5K_NODCU_RETRY_LMT_LG_RETRY)
+ | AR5K_REG_SM(ah->ah_retry_short,
+ AR5K_NODCU_RETRY_LMT_SH_RETRY),
+ AR5K_NODCU_RETRY_LMT);
+ /* DCU on AR5211+ */
+ } else {
+ ath5k_hw_reg_write(ah,
+ AR5K_REG_SM(ah->ah_retry_long,
+ AR5K_DCU_RETRY_LMT_RTS)
+ | AR5K_REG_SM(ah->ah_retry_long,
+ AR5K_DCU_RETRY_LMT_STA_RTS)
+ | AR5K_REG_SM(max(ah->ah_retry_long, ah->ah_retry_short),
+ AR5K_DCU_RETRY_LMT_STA_DATA),
+ AR5K_QUEUE_DFS_RETRY_LIMIT(queue));
+ }
+}
+
+/**
+ * ath5k_hw_reset_tx_queue - Initialize a single hw queue
+ *
+ * @ah The &struct ath5k_hw
+ * @queue The hw queue number
+ *
+ * Set DFS properties for the given transmit queue on DCU
+ * and configures all queue-specific parameters.
+ */
+int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
+{
+ struct ath5k_txq_info *tq = &ah->ah_txq[queue];
+
+ AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
+
+ tq = &ah->ah_txq[queue];
+
+ /* Skip if queue inactive or if we are on AR5210
+ * that doesn't have QCU/DCU */
+ if ((ah->ah_version == AR5K_AR5210) ||
+ (tq->tqi_type == AR5K_TX_QUEUE_INACTIVE))
+ return 0;
+
+ /*
+ * Set contention window (cw_min/cw_max)
+ * and arbitrated interframe space (aifs)...
+ */
+ ath5k_hw_reg_write(ah,
+ AR5K_REG_SM(tq->tqi_cw_min, AR5K_DCU_LCL_IFS_CW_MIN) |
+ AR5K_REG_SM(tq->tqi_cw_max, AR5K_DCU_LCL_IFS_CW_MAX) |
+ AR5K_REG_SM(tq->tqi_aifs, AR5K_DCU_LCL_IFS_AIFS),
+ AR5K_QUEUE_DFS_LOCAL_IFS(queue));
+
+ /*
+ * Set tx retry limits for this queue
+ */
+ ath5k_hw_set_tx_retry_limits(ah, queue);
+
+
+ /*
+ * Set misc registers
+ */
+
+ /* Enable DCU to wait for next fragment from QCU */
+ AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
+ AR5K_DCU_MISC_FRAG_WAIT);
+
+ /* On Maui and Spirit use the global seqnum on DCU */
+ if (ah->ah_mac_version < AR5K_SREV_AR5211)
+ AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
+ AR5K_DCU_MISC_SEQNUM_CTL);
+
+ /* Constant bit rate period */
+ if (tq->tqi_cbr_period) {
+ ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_cbr_period,
+ AR5K_QCU_CBRCFG_INTVAL) |
+ AR5K_REG_SM(tq->tqi_cbr_overflow_limit,
+ AR5K_QCU_CBRCFG_ORN_THRES),
+ AR5K_QUEUE_CBRCFG(queue));
+
+ AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
+ AR5K_QCU_MISC_FRSHED_CBR);
+
+ if (tq->tqi_cbr_overflow_limit)
+ AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
+ AR5K_QCU_MISC_CBR_THRES_ENABLE);
+ }
+
+ /* Ready time interval */
+ if (tq->tqi_ready_time && (tq->tqi_type != AR5K_TX_QUEUE_CAB))
+ ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_ready_time,
+ AR5K_QCU_RDYTIMECFG_INTVAL) |
+ AR5K_QCU_RDYTIMECFG_ENABLE,
+ AR5K_QUEUE_RDYTIMECFG(queue));
+
+ if (tq->tqi_burst_time) {
+ ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_burst_time,
+ AR5K_DCU_CHAN_TIME_DUR) |
+ AR5K_DCU_CHAN_TIME_ENABLE,
+ AR5K_QUEUE_DFS_CHANNEL_TIME(queue));
+
+ if (tq->tqi_flags & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE)
+ AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
+ AR5K_QCU_MISC_RDY_VEOL_POLICY);
+ }
+
+ /* Enable/disable Post frame backoff */
+ if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE)
+ ath5k_hw_reg_write(ah, AR5K_DCU_MISC_POST_FR_BKOFF_DIS,
+ AR5K_QUEUE_DFS_MISC(queue));
+
+ /* Enable/disable fragmentation burst backoff */
+ if (tq->tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE)
+ ath5k_hw_reg_write(ah, AR5K_DCU_MISC_BACKOFF_FRAG,
+ AR5K_QUEUE_DFS_MISC(queue));
+
+ /*
+ * Set registers by queue type
+ */
+ switch (tq->tqi_type) {
+ case AR5K_TX_QUEUE_BEACON:
+ AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
+ AR5K_QCU_MISC_FRSHED_DBA_GT |
+ AR5K_QCU_MISC_CBREXP_BCN_DIS |
+ AR5K_QCU_MISC_BCN_ENABLE);
+
+ AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
+ (AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL <<
+ AR5K_DCU_MISC_ARBLOCK_CTL_S) |
+ AR5K_DCU_MISC_ARBLOCK_IGNORE |
+ AR5K_DCU_MISC_POST_FR_BKOFF_DIS |
+ AR5K_DCU_MISC_BCN_ENABLE);
+ break;
+
+ case AR5K_TX_QUEUE_CAB:
+ /* XXX: use BCN_SENT_GT, if we can figure out how */
+ AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
+ AR5K_QCU_MISC_FRSHED_DBA_GT |
+ AR5K_QCU_MISC_CBREXP_DIS |
+ AR5K_QCU_MISC_CBREXP_BCN_DIS);
+
+ ath5k_hw_reg_write(ah, ((tq->tqi_ready_time -
+ (AR5K_TUNE_SW_BEACON_RESP -
+ AR5K_TUNE_DMA_BEACON_RESP) -
+ AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF) * 1024) |
+ AR5K_QCU_RDYTIMECFG_ENABLE,
+ AR5K_QUEUE_RDYTIMECFG(queue));
+
+ AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
+ (AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL <<
+ AR5K_DCU_MISC_ARBLOCK_CTL_S));
+ break;
+
+ case AR5K_TX_QUEUE_UAPSD:
+ AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
+ AR5K_QCU_MISC_CBREXP_DIS);
+ break;
+
+ case AR5K_TX_QUEUE_DATA:
+ default:
+ break;
+ }
+
+ /* TODO: Handle frame compression */
+
+ /*
+ * Enable interrupts for this tx queue
+ * in the secondary interrupt mask registers
+ */
+ if (tq->tqi_flags & AR5K_TXQ_FLAG_TXOKINT_ENABLE)
+ AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txok, queue);
+
+ if (tq->tqi_flags & AR5K_TXQ_FLAG_TXERRINT_ENABLE)
+ AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txerr, queue);
+
+ if (tq->tqi_flags & AR5K_TXQ_FLAG_TXURNINT_ENABLE)
+ AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txurn, queue);
+
+ if (tq->tqi_flags & AR5K_TXQ_FLAG_TXDESCINT_ENABLE)
+ AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txdesc, queue);
+
+ if (tq->tqi_flags & AR5K_TXQ_FLAG_TXEOLINT_ENABLE)
+ AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txeol, queue);
+
+ if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRORNINT_ENABLE)
+ AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrorn, queue);
+
+ if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRURNINT_ENABLE)
+ AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrurn, queue);
+
+ if (tq->tqi_flags & AR5K_TXQ_FLAG_QTRIGINT_ENABLE)
+ AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_qtrig, queue);
+
+ if (tq->tqi_flags & AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE)
+ AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_nofrm, queue);
+
+ /* Update secondary interrupt mask registers */
+
+ /* Filter out inactive queues */
+ ah->ah_txq_imr_txok &= ah->ah_txq_status;
+ ah->ah_txq_imr_txerr &= ah->ah_txq_status;
+ ah->ah_txq_imr_txurn &= ah->ah_txq_status;
+ ah->ah_txq_imr_txdesc &= ah->ah_txq_status;
+ ah->ah_txq_imr_txeol &= ah->ah_txq_status;
+ ah->ah_txq_imr_cbrorn &= ah->ah_txq_status;
+ ah->ah_txq_imr_cbrurn &= ah->ah_txq_status;
+ ah->ah_txq_imr_qtrig &= ah->ah_txq_status;
+ ah->ah_txq_imr_nofrm &= ah->ah_txq_status;
+
+ ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txok,
+ AR5K_SIMR0_QCU_TXOK) |
+ AR5K_REG_SM(ah->ah_txq_imr_txdesc,
+ AR5K_SIMR0_QCU_TXDESC),
+ AR5K_SIMR0);
+
+ ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txerr,
+ AR5K_SIMR1_QCU_TXERR) |
+ AR5K_REG_SM(ah->ah_txq_imr_txeol,
+ AR5K_SIMR1_QCU_TXEOL),
+ AR5K_SIMR1);
+
+ /* Update SIMR2 but don't overwrite rest simr2 settings */
+ AR5K_REG_DISABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_QCU_TXURN);
+ AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2,
+ AR5K_REG_SM(ah->ah_txq_imr_txurn,
+ AR5K_SIMR2_QCU_TXURN));
+
+ ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_cbrorn,
+ AR5K_SIMR3_QCBRORN) |
+ AR5K_REG_SM(ah->ah_txq_imr_cbrurn,
+ AR5K_SIMR3_QCBRURN),
+ AR5K_SIMR3);
+
+ ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_qtrig,
+ AR5K_SIMR4_QTRIG), AR5K_SIMR4);
+
+ /* Set TXNOFRM_QCU for the queues with TXNOFRM enabled */
+ ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_nofrm,
+ AR5K_TXNOFRM_QCU), AR5K_TXNOFRM);
+
+ /* No queue has TXNOFRM enabled, disable the interrupt
+ * by setting AR5K_TXNOFRM to zero */
+ if (ah->ah_txq_imr_nofrm == 0)
+ ath5k_hw_reg_write(ah, 0, AR5K_TXNOFRM);
+
+ /* Set QCU mask for this DCU to save power */
+ AR5K_REG_WRITE_Q(ah, AR5K_QUEUE_QCUMASK(queue), queue);
+
+ return 0;
+}
+
+
+/**************************\
+* Global QCU/DCU functions *
+\**************************/
+
+/**
+ * ath5k_hw_set_ifs_intervals - Set global inter-frame spaces on DCU
+ *
+ * @ah The &struct ath5k_hw
+ * @slot_time Slot time in us
+ *
+ * Sets the global IFS intervals on DCU (also works on AR5210) for
+ * the given slot time and the current bwmode.
+ */
+int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time)
+{
+ struct ieee80211_channel *channel = ah->ah_current_channel;
+ struct ieee80211_rate *rate;
+ u32 ack_tx_time, eifs, eifs_clock, sifs, sifs_clock;
+ u32 slot_time_clock = ath5k_hw_htoclock(ah, slot_time);
+
+ if (slot_time < 6 || slot_time_clock > AR5K_SLOT_TIME_MAX)
+ return -EINVAL;
+
+ sifs = ath5k_hw_get_default_sifs(ah);
+ sifs_clock = ath5k_hw_htoclock(ah, sifs - 2);
+
+ /* EIFS
+ * Txtime of ack at lowest rate + SIFS + DIFS
+ * (DIFS = SIFS + 2 * Slot time)
+ *
+ * Note: HAL has some predefined values for EIFS
+ * Turbo: (37 + 2 * 6)
+ * Default: (74 + 2 * 9)
+ * Half: (149 + 2 * 13)
+ * Quarter: (298 + 2 * 21)
+ *
+ * (74 + 2 * 6) for AR5210 default and turbo !
+ *
+ * According to the formula we have
+ * ack_tx_time = 25 for turbo and
+ * ack_tx_time = 42.5 * clock multiplier
+ * for default/half/quarter.
+ *
+ * This can't be right, 42 is what we would get
+ * from ath5k_hw_get_frame_dur_for_bwmode or
+ * ieee80211_generic_frame_duration for zero frame
+ * length and without SIFS !
+ *
+ * Also we have different lowest rate for 802.11a
+ */
+ if (channel->band == IEEE80211_BAND_5GHZ)
+ rate = &ah->sbands[IEEE80211_BAND_5GHZ].bitrates[0];
+ else
+ rate = &ah->sbands[IEEE80211_BAND_2GHZ].bitrates[0];
+
+ ack_tx_time = ath5k_hw_get_frame_duration(ah, 10, rate, false);
+
+ /* ack_tx_time includes an SIFS already */
+ eifs = ack_tx_time + sifs + 2 * slot_time;
+ eifs_clock = ath5k_hw_htoclock(ah, eifs);
+
+ /* Set IFS settings on AR5210 */
+ if (ah->ah_version == AR5K_AR5210) {
+ u32 pifs, pifs_clock, difs, difs_clock;
+
+ /* Set slot time */
+ ath5k_hw_reg_write(ah, slot_time_clock, AR5K_SLOT_TIME);
+
+ /* Set EIFS */
+ eifs_clock = AR5K_REG_SM(eifs_clock, AR5K_IFS1_EIFS);
+
+ /* PIFS = Slot time + SIFS */
+ pifs = slot_time + sifs;
+ pifs_clock = ath5k_hw_htoclock(ah, pifs);
+ pifs_clock = AR5K_REG_SM(pifs_clock, AR5K_IFS1_PIFS);
+
+ /* DIFS = SIFS + 2 * Slot time */
+ difs = sifs + 2 * slot_time;
+ difs_clock = ath5k_hw_htoclock(ah, difs);
+
+ /* Set SIFS/DIFS */
+ ath5k_hw_reg_write(ah, (difs_clock <<
+ AR5K_IFS0_DIFS_S) | sifs_clock,
+ AR5K_IFS0);
+
+ /* Set PIFS/EIFS and preserve AR5K_INIT_CARR_SENSE_EN */
+ ath5k_hw_reg_write(ah, pifs_clock | eifs_clock |
+ (AR5K_INIT_CARR_SENSE_EN << AR5K_IFS1_CS_EN_S),
+ AR5K_IFS1);
+
+ return 0;
+ }
+
+ /* Set IFS slot time */
+ ath5k_hw_reg_write(ah, slot_time_clock, AR5K_DCU_GBL_IFS_SLOT);
+
+ /* Set EIFS interval */
+ ath5k_hw_reg_write(ah, eifs_clock, AR5K_DCU_GBL_IFS_EIFS);
+
+ /* Set SIFS interval in usecs */
+ AR5K_REG_WRITE_BITS(ah, AR5K_DCU_GBL_IFS_MISC,
+ AR5K_DCU_GBL_IFS_MISC_SIFS_DUR_USEC,
+ sifs);
+
+ /* Set SIFS interval in clock cycles */
+ ath5k_hw_reg_write(ah, sifs_clock, AR5K_DCU_GBL_IFS_SIFS);
+
+ return 0;
+}
+
+
+int ath5k_hw_init_queues(struct ath5k_hw *ah)
+{
+ int i, ret;
+
+ /* TODO: HW Compression support for data queues */
+ /* TODO: Burst prefetch for data queues */
+
+ /*
+ * Reset queues and start beacon timers at the end of the reset routine
+ * This also sets QCU mask on each DCU for 1:1 qcu to dcu mapping
+ * Note: If we want we can assign multiple qcus on one dcu.
+ */
+ if (ah->ah_version != AR5K_AR5210)
+ for (i = 0; i < ah->ah_capabilities.cap_queues.q_tx_num; i++) {
+ ret = ath5k_hw_reset_tx_queue(ah, i);
+ if (ret) {
+ ATH5K_ERR(ah,
+ "failed to reset TX queue #%d\n", i);
+ return ret;
+ }
+ }
+ else
+ /* No QCU/DCU on AR5210, just set tx
+ * retry limits. We set IFS parameters
+ * on ath5k_hw_set_ifs_intervals */
+ ath5k_hw_set_tx_retry_limits(ah, 0);
+
+ /* Set the turbo flag when operating on 40MHz */
+ if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
+ AR5K_REG_ENABLE_BITS(ah, AR5K_DCU_GBL_IFS_MISC,
+ AR5K_DCU_GBL_IFS_MISC_TURBO_MODE);
+
+ /* If we didn't set IFS timings through
+ * ath5k_hw_set_coverage_class make sure
+ * we set them here */
+ if (!ah->ah_coverage_class) {
+ unsigned int slot_time = ath5k_hw_get_default_slottime(ah);
+ ath5k_hw_set_ifs_intervals(ah, slot_time);
+ }
+
+ return 0;
+}
diff --git a/drivers/net/wireless/ath/ath5k/reg.h b/drivers/net/wireless/ath/ath5k/reg.h
new file mode 100644
index 0000000..f5c1000
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/reg.h
@@ -0,0 +1,2589 @@
+/*
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2007-2008 Michael Taylor <mike.taylor@apprion.com>
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ */
+
+/*
+ * Register values for Atheros 5210/5211/5212 cards from OpenBSD's ar5k
+ * maintained by Reyk Floeter
+ *
+ * I tried to document those registers by looking at ar5k code, some
+ * 802.11 (802.11e mostly) papers and by reading various public available
+ * Atheros presentations and papers like these:
+ *
+ * 5210 - http://nova.stanford.edu/~bbaas/ps/isscc2002_slides.pdf
+ *
+ * 5211 - http://www.hotchips.org/archives/hc14/3_Tue/16_mcfarland.pdf
+ *
+ * This file also contains register values found on a memory dump of
+ * Atheros's ART program (Atheros Radio Test), on ath9k, on legacy-hal
+ * released by Atheros and on various debug messages found on the net.
+ */
+
+#include "../reg.h"
+
+/*====MAC DMA REGISTERS====*/
+
+/*
+ * AR5210-Specific TXDP registers
+ * 5210 has only 2 transmit queues so no DCU/QCU, just
+ * 2 transmit descriptor pointers...
+ */
+#define AR5K_NOQCU_TXDP0 0x0000 /* Queue 0 - data */
+#define AR5K_NOQCU_TXDP1 0x0004 /* Queue 1 - beacons */
+
+/*
+ * Mac Control Register
+ */
+#define AR5K_CR 0x0008 /* Register Address */
+#define AR5K_CR_TXE0 0x00000001 /* TX Enable for queue 0 on 5210 */
+#define AR5K_CR_TXE1 0x00000002 /* TX Enable for queue 1 on 5210 */
+#define AR5K_CR_RXE 0x00000004 /* RX Enable */
+#define AR5K_CR_TXD0 0x00000008 /* TX Disable for queue 0 on 5210 */
+#define AR5K_CR_TXD1 0x00000010 /* TX Disable for queue 1 on 5210 */
+#define AR5K_CR_RXD 0x00000020 /* RX Disable */
+#define AR5K_CR_SWI 0x00000040 /* Software Interrupt */
+
+/*
+ * RX Descriptor Pointer register
+ */
+#define AR5K_RXDP 0x000c
+
+/*
+ * Configuration and status register
+ */
+#define AR5K_CFG 0x0014 /* Register Address */
+#define AR5K_CFG_SWTD 0x00000001 /* Byte-swap TX descriptor (for big endian archs) */
+#define AR5K_CFG_SWTB 0x00000002 /* Byte-swap TX buffer */
+#define AR5K_CFG_SWRD 0x00000004 /* Byte-swap RX descriptor */
+#define AR5K_CFG_SWRB 0x00000008 /* Byte-swap RX buffer */
+#define AR5K_CFG_SWRG 0x00000010 /* Byte-swap Register access */
+#define AR5K_CFG_IBSS 0x00000020 /* 0-BSS, 1-IBSS [5211+] */
+#define AR5K_CFG_PHY_OK 0x00000100 /* [5211+] */
+#define AR5K_CFG_EEBS 0x00000200 /* EEPROM is busy */
+#define AR5K_CFG_CLKGD 0x00000400 /* Clock gated (Disable dynamic clock) */
+#define AR5K_CFG_TXCNT 0x00007800 /* Tx frame count (?) [5210] */
+#define AR5K_CFG_TXCNT_S 11
+#define AR5K_CFG_TXFSTAT 0x00008000 /* Tx frame status (?) [5210] */
+#define AR5K_CFG_TXFSTRT 0x00010000 /* [5210] */
+#define AR5K_CFG_PCI_THRES 0x00060000 /* PCI Master req q threshold [5211+] */
+#define AR5K_CFG_PCI_THRES_S 17
+
+/*
+ * Interrupt enable register
+ */
+#define AR5K_IER 0x0024 /* Register Address */
+#define AR5K_IER_DISABLE 0x00000000 /* Disable card interrupts */
+#define AR5K_IER_ENABLE 0x00000001 /* Enable card interrupts */
+
+
+/*
+ * 0x0028 is Beacon Control Register on 5210
+ * and first RTS duration register on 5211
+ */
+
+/*
+ * Beacon control register [5210]
+ */
+#define AR5K_BCR 0x0028 /* Register Address */
+#define AR5K_BCR_AP 0x00000000 /* AP mode */
+#define AR5K_BCR_ADHOC 0x00000001 /* Ad-Hoc mode */
+#define AR5K_BCR_BDMAE 0x00000002 /* DMA enable */
+#define AR5K_BCR_TQ1FV 0x00000004 /* Use Queue1 for CAB traffic */
+#define AR5K_BCR_TQ1V 0x00000008 /* Use Queue1 for Beacon traffic */
+#define AR5K_BCR_BCGET 0x00000010
+
+/*
+ * First RTS duration register [5211]
+ */
+#define AR5K_RTSD0 0x0028 /* Register Address */
+#define AR5K_RTSD0_6 0x000000ff /* 6Mb RTS duration mask (?) */
+#define AR5K_RTSD0_6_S 0 /* 6Mb RTS duration shift (?) */
+#define AR5K_RTSD0_9 0x0000ff00 /* 9Mb*/
+#define AR5K_RTSD0_9_S 8
+#define AR5K_RTSD0_12 0x00ff0000 /* 12Mb*/
+#define AR5K_RTSD0_12_S 16
+#define AR5K_RTSD0_18 0xff000000 /* 16Mb*/
+#define AR5K_RTSD0_18_S 24
+
+
+/*
+ * 0x002c is Beacon Status Register on 5210
+ * and second RTS duration register on 5211
+ */
+
+/*
+ * Beacon status register [5210]
+ *
+ * As i can see in ar5k_ar5210_tx_start Reyk uses some of the values of BCR
+ * for this register, so i guess TQ1V,TQ1FV and BDMAE have the same meaning
+ * here and SNP/SNAP means "snapshot" (so this register gets synced with BCR).
+ * So SNAPPEDBCRVALID should also stand for "snapped BCR -values- valid", so i
+ * renamed it to SNAPSHOTSVALID to make more sense. I really have no idea what
+ * else can it be. I also renamed SNPBCMD to SNPADHOC to match BCR.
+ */
+#define AR5K_BSR 0x002c /* Register Address */
+#define AR5K_BSR_BDLYSW 0x00000001 /* SW Beacon delay (?) */
+#define AR5K_BSR_BDLYDMA 0x00000002 /* DMA Beacon delay (?) */
+#define AR5K_BSR_TXQ1F 0x00000004 /* Beacon queue (1) finished */
+#define AR5K_BSR_ATIMDLY 0x00000008 /* ATIM delay (?) */
+#define AR5K_BSR_SNPADHOC 0x00000100 /* Ad-hoc mode set (?) */
+#define AR5K_BSR_SNPBDMAE 0x00000200 /* Beacon DMA enabled (?) */
+#define AR5K_BSR_SNPTQ1FV 0x00000400 /* Queue1 is used for CAB traffic (?) */
+#define AR5K_BSR_SNPTQ1V 0x00000800 /* Queue1 is used for Beacon traffic (?) */
+#define AR5K_BSR_SNAPSHOTSVALID 0x00001000 /* BCR snapshots are valid (?) */
+#define AR5K_BSR_SWBA_CNT 0x00ff0000
+
+/*
+ * Second RTS duration register [5211]
+ */
+#define AR5K_RTSD1 0x002c /* Register Address */
+#define AR5K_RTSD1_24 0x000000ff /* 24Mb */
+#define AR5K_RTSD1_24_S 0
+#define AR5K_RTSD1_36 0x0000ff00 /* 36Mb */
+#define AR5K_RTSD1_36_S 8
+#define AR5K_RTSD1_48 0x00ff0000 /* 48Mb */
+#define AR5K_RTSD1_48_S 16
+#define AR5K_RTSD1_54 0xff000000 /* 54Mb */
+#define AR5K_RTSD1_54_S 24
+
+
+/*
+ * Transmit configuration register
+ */
+#define AR5K_TXCFG 0x0030 /* Register Address */
+#define AR5K_TXCFG_SDMAMR 0x00000007 /* DMA size (read) */
+#define AR5K_TXCFG_SDMAMR_S 0
+#define AR5K_TXCFG_B_MODE 0x00000008 /* Set b mode for 5111 (enable 2111) */
+#define AR5K_TXCFG_TXFSTP 0x00000008 /* TX DMA full Stop [5210] */
+#define AR5K_TXCFG_TXFULL 0x000003f0 /* TX Trigger level mask */
+#define AR5K_TXCFG_TXFULL_S 4
+#define AR5K_TXCFG_TXFULL_0B 0x00000000
+#define AR5K_TXCFG_TXFULL_64B 0x00000010
+#define AR5K_TXCFG_TXFULL_128B 0x00000020
+#define AR5K_TXCFG_TXFULL_192B 0x00000030
+#define AR5K_TXCFG_TXFULL_256B 0x00000040
+#define AR5K_TXCFG_TXCONT_EN 0x00000080
+#define AR5K_TXCFG_DMASIZE 0x00000100 /* Flag for passing DMA size [5210] */
+#define AR5K_TXCFG_JUMBO_DESC_EN 0x00000400 /* Enable jumbo tx descriptors [5211+] */
+#define AR5K_TXCFG_ADHOC_BCN_ATIM 0x00000800 /* Adhoc Beacon ATIM Policy */
+#define AR5K_TXCFG_ATIM_WINDOW_DEF_DIS 0x00001000 /* Disable ATIM window defer [5211+] */
+#define AR5K_TXCFG_RTSRND 0x00001000 /* [5211+] */
+#define AR5K_TXCFG_FRMPAD_DIS 0x00002000 /* [5211+] */
+#define AR5K_TXCFG_RDY_CBR_DIS 0x00004000 /* Ready time CBR disable [5211+] */
+#define AR5K_TXCFG_JUMBO_FRM_MODE 0x00008000 /* Jumbo frame mode [5211+] */
+#define AR5K_TXCFG_DCU_DBL_BUF_DIS 0x00008000 /* Disable double buffering on DCU */
+#define AR5K_TXCFG_DCU_CACHING_DIS 0x00010000 /* Disable DCU caching */
+
+/*
+ * Receive configuration register
+ */
+#define AR5K_RXCFG 0x0034 /* Register Address */
+#define AR5K_RXCFG_SDMAMW 0x00000007 /* DMA size (write) */
+#define AR5K_RXCFG_SDMAMW_S 0
+#define AR5K_RXCFG_ZLFDMA 0x00000008 /* Enable Zero-length frame DMA */
+#define AR5K_RXCFG_DEF_ANTENNA 0x00000010 /* Default antenna (?) */
+#define AR5K_RXCFG_JUMBO_RXE 0x00000020 /* Enable jumbo rx descriptors [5211+] */
+#define AR5K_RXCFG_JUMBO_WRAP 0x00000040 /* Wrap jumbo frames [5211+] */
+#define AR5K_RXCFG_SLE_ENTRY 0x00000080 /* Sleep entry policy */
+
+/*
+ * Receive jumbo descriptor last address register
+ * Only found in 5211 (?)
+ */
+#define AR5K_RXJLA 0x0038
+
+/*
+ * MIB control register
+ */
+#define AR5K_MIBC 0x0040 /* Register Address */
+#define AR5K_MIBC_COW 0x00000001 /* Counter Overflow Warning */
+#define AR5K_MIBC_FMC 0x00000002 /* Freeze MIB Counters */
+#define AR5K_MIBC_CMC 0x00000004 /* Clear MIB Counters */
+#define AR5K_MIBC_MCS 0x00000008 /* MIB counter strobe, increment all */
+
+/*
+ * Timeout prescale register
+ */
+#define AR5K_TOPS 0x0044
+#define AR5K_TOPS_M 0x0000ffff
+
+/*
+ * Receive timeout register (no frame received)
+ */
+#define AR5K_RXNOFRM 0x0048
+#define AR5K_RXNOFRM_M 0x000003ff
+
+/*
+ * Transmit timeout register (no frame sent)
+ */
+#define AR5K_TXNOFRM 0x004c
+#define AR5K_TXNOFRM_M 0x000003ff
+#define AR5K_TXNOFRM_QCU 0x000ffc00
+#define AR5K_TXNOFRM_QCU_S 10
+
+/*
+ * Receive frame gap timeout register
+ */
+#define AR5K_RPGTO 0x0050
+#define AR5K_RPGTO_M 0x000003ff
+
+/*
+ * Receive frame count limit register
+ */
+#define AR5K_RFCNT 0x0054
+#define AR5K_RFCNT_M 0x0000001f /* [5211+] (?) */
+#define AR5K_RFCNT_RFCL 0x0000000f /* [5210] */
+
+/*
+ * Misc settings register
+ * (reserved0-3)
+ */
+#define AR5K_MISC 0x0058 /* Register Address */
+#define AR5K_MISC_DMA_OBS_M 0x000001e0
+#define AR5K_MISC_DMA_OBS_S 5
+#define AR5K_MISC_MISC_OBS_M 0x00000e00
+#define AR5K_MISC_MISC_OBS_S 9
+#define AR5K_MISC_MAC_OBS_LSB_M 0x00007000
+#define AR5K_MISC_MAC_OBS_LSB_S 12
+#define AR5K_MISC_MAC_OBS_MSB_M 0x00038000
+#define AR5K_MISC_MAC_OBS_MSB_S 15
+#define AR5K_MISC_LED_DECAY 0x001c0000 /* [5210] */
+#define AR5K_MISC_LED_BLINK 0x00e00000 /* [5210] */
+
+/*
+ * QCU/DCU clock gating register (5311)
+ * (reserved4-5)
+ */
+#define AR5K_QCUDCU_CLKGT 0x005c /* Register Address (?) */
+#define AR5K_QCUDCU_CLKGT_QCU 0x0000ffff /* Mask for QCU clock */
+#define AR5K_QCUDCU_CLKGT_DCU 0x07ff0000 /* Mask for DCU clock */
+
+/*
+ * Interrupt Status Registers
+ *
+ * For 5210 there is only one status register but for
+ * 5211/5212 we have one primary and 4 secondary registers.
+ * So we have AR5K_ISR for 5210 and AR5K_PISR /SISRx for 5211/5212.
+ * Most of these bits are common for all chipsets.
+ */
+#define AR5K_ISR 0x001c /* Register Address [5210] */
+#define AR5K_PISR 0x0080 /* Register Address [5211+] */
+#define AR5K_ISR_RXOK 0x00000001 /* Frame successfully received */
+#define AR5K_ISR_RXDESC 0x00000002 /* RX descriptor request */
+#define AR5K_ISR_RXERR 0x00000004 /* Receive error */
+#define AR5K_ISR_RXNOFRM 0x00000008 /* No frame received (receive timeout) */
+#define AR5K_ISR_RXEOL 0x00000010 /* Empty RX descriptor */
+#define AR5K_ISR_RXORN 0x00000020 /* Receive FIFO overrun */
+#define AR5K_ISR_TXOK 0x00000040 /* Frame successfully transmitted */
+#define AR5K_ISR_TXDESC 0x00000080 /* TX descriptor request */
+#define AR5K_ISR_TXERR 0x00000100 /* Transmit error */
+#define AR5K_ISR_TXNOFRM 0x00000200 /* No frame transmitted (transmit timeout) */
+#define AR5K_ISR_TXEOL 0x00000400 /* Empty TX descriptor */
+#define AR5K_ISR_TXURN 0x00000800 /* Transmit FIFO underrun */
+#define AR5K_ISR_MIB 0x00001000 /* Update MIB counters */
+#define AR5K_ISR_SWI 0x00002000 /* Software interrupt */
+#define AR5K_ISR_RXPHY 0x00004000 /* PHY error */
+#define AR5K_ISR_RXKCM 0x00008000 /* RX Key cache miss */
+#define AR5K_ISR_SWBA 0x00010000 /* Software beacon alert */
+#define AR5K_ISR_BRSSI 0x00020000 /* Beacon rssi below threshold (?) */
+#define AR5K_ISR_BMISS 0x00040000 /* Beacon missed */
+#define AR5K_ISR_HIUERR 0x00080000 /* Host Interface Unit error [5211+] */
+#define AR5K_ISR_BNR 0x00100000 /* Beacon not ready [5211+] */
+#define AR5K_ISR_MCABT 0x00100000 /* Master Cycle Abort [5210] */
+#define AR5K_ISR_RXCHIRP 0x00200000 /* CHIRP Received [5212+] */
+#define AR5K_ISR_SSERR 0x00200000 /* Signaled System Error [5210] */
+#define AR5K_ISR_DPERR 0x00400000 /* Det par Error (?) [5210] */
+#define AR5K_ISR_RXDOPPLER 0x00400000 /* Doppler chirp received [5212+] */
+#define AR5K_ISR_TIM 0x00800000 /* [5211+] */
+#define AR5K_ISR_BCNMISC 0x00800000 /* 'or' of TIM, CAB_END, DTIM_SYNC, BCN_TIMEOUT,
+ CAB_TIMEOUT and DTIM bits from SISR2 [5212+] */
+#define AR5K_ISR_GPIO 0x01000000 /* GPIO (rf kill) */
+#define AR5K_ISR_QCBRORN 0x02000000 /* QCU CBR overrun [5211+] */
+#define AR5K_ISR_QCBRURN 0x04000000 /* QCU CBR underrun [5211+] */
+#define AR5K_ISR_QTRIG 0x08000000 /* QCU scheduling trigger [5211+] */
+
+/*
+ * Secondary status registers [5211+] (0 - 4)
+ *
+ * These give the status for each QCU, only QCUs 0-9 are
+ * represented.
+ */
+#define AR5K_SISR0 0x0084 /* Register Address [5211+] */
+#define AR5K_SISR0_QCU_TXOK 0x000003ff /* Mask for QCU_TXOK */
+#define AR5K_SISR0_QCU_TXOK_S 0
+#define AR5K_SISR0_QCU_TXDESC 0x03ff0000 /* Mask for QCU_TXDESC */
+#define AR5K_SISR0_QCU_TXDESC_S 16
+
+#define AR5K_SISR1 0x0088 /* Register Address [5211+] */
+#define AR5K_SISR1_QCU_TXERR 0x000003ff /* Mask for QCU_TXERR */
+#define AR5K_SISR1_QCU_TXERR_S 0
+#define AR5K_SISR1_QCU_TXEOL 0x03ff0000 /* Mask for QCU_TXEOL */
+#define AR5K_SISR1_QCU_TXEOL_S 16
+
+#define AR5K_SISR2 0x008c /* Register Address [5211+] */
+#define AR5K_SISR2_QCU_TXURN 0x000003ff /* Mask for QCU_TXURN */
+#define AR5K_SISR2_QCU_TXURN_S 0
+#define AR5K_SISR2_MCABT 0x00010000 /* Master Cycle Abort */
+#define AR5K_SISR2_SSERR 0x00020000 /* Signaled System Error */
+#define AR5K_SISR2_DPERR 0x00040000 /* Bus parity error */
+#define AR5K_SISR2_TIM 0x01000000 /* [5212+] */
+#define AR5K_SISR2_CAB_END 0x02000000 /* [5212+] */
+#define AR5K_SISR2_DTIM_SYNC 0x04000000 /* DTIM sync lost [5212+] */
+#define AR5K_SISR2_BCN_TIMEOUT 0x08000000 /* Beacon Timeout [5212+] */
+#define AR5K_SISR2_CAB_TIMEOUT 0x10000000 /* CAB Timeout [5212+] */
+#define AR5K_SISR2_DTIM 0x20000000 /* [5212+] */
+#define AR5K_SISR2_TSFOOR 0x80000000 /* TSF OOR (?) */
+
+#define AR5K_SISR3 0x0090 /* Register Address [5211+] */
+#define AR5K_SISR3_QCBRORN 0x000003ff /* Mask for QCBRORN */
+#define AR5K_SISR3_QCBRORN_S 0
+#define AR5K_SISR3_QCBRURN 0x03ff0000 /* Mask for QCBRURN */
+#define AR5K_SISR3_QCBRURN_S 16
+
+#define AR5K_SISR4 0x0094 /* Register Address [5211+] */
+#define AR5K_SISR4_QTRIG 0x000003ff /* Mask for QTRIG */
+#define AR5K_SISR4_QTRIG_S 0
+
+/*
+ * Shadow read-and-clear interrupt status registers [5211+]
+ */
+#define AR5K_RAC_PISR 0x00c0 /* Read and clear PISR */
+#define AR5K_RAC_SISR0 0x00c4 /* Read and clear SISR0 */
+#define AR5K_RAC_SISR1 0x00c8 /* Read and clear SISR1 */
+#define AR5K_RAC_SISR2 0x00cc /* Read and clear SISR2 */
+#define AR5K_RAC_SISR3 0x00d0 /* Read and clear SISR3 */
+#define AR5K_RAC_SISR4 0x00d4 /* Read and clear SISR4 */
+
+/*
+ * Interrupt Mask Registers
+ *
+ * As with ISRs 5210 has one IMR (AR5K_IMR) and 5211/5212 has one primary
+ * (AR5K_PIMR) and 4 secondary IMRs (AR5K_SIMRx). Note that ISR/IMR flags match.
+ */
+#define AR5K_IMR 0x0020 /* Register Address [5210] */
+#define AR5K_PIMR 0x00a0 /* Register Address [5211+] */
+#define AR5K_IMR_RXOK 0x00000001 /* Frame successfully received*/
+#define AR5K_IMR_RXDESC 0x00000002 /* RX descriptor request*/
+#define AR5K_IMR_RXERR 0x00000004 /* Receive error*/
+#define AR5K_IMR_RXNOFRM 0x00000008 /* No frame received (receive timeout)*/
+#define AR5K_IMR_RXEOL 0x00000010 /* Empty RX descriptor*/
+#define AR5K_IMR_RXORN 0x00000020 /* Receive FIFO overrun*/
+#define AR5K_IMR_TXOK 0x00000040 /* Frame successfully transmitted*/
+#define AR5K_IMR_TXDESC 0x00000080 /* TX descriptor request*/
+#define AR5K_IMR_TXERR 0x00000100 /* Transmit error*/
+#define AR5K_IMR_TXNOFRM 0x00000200 /* No frame transmitted (transmit timeout)*/
+#define AR5K_IMR_TXEOL 0x00000400 /* Empty TX descriptor*/
+#define AR5K_IMR_TXURN 0x00000800 /* Transmit FIFO underrun*/
+#define AR5K_IMR_MIB 0x00001000 /* Update MIB counters*/
+#define AR5K_IMR_SWI 0x00002000 /* Software interrupt */
+#define AR5K_IMR_RXPHY 0x00004000 /* PHY error*/
+#define AR5K_IMR_RXKCM 0x00008000 /* RX Key cache miss */
+#define AR5K_IMR_SWBA 0x00010000 /* Software beacon alert*/
+#define AR5K_IMR_BRSSI 0x00020000 /* Beacon rssi below threshold (?) */
+#define AR5K_IMR_BMISS 0x00040000 /* Beacon missed*/
+#define AR5K_IMR_HIUERR 0x00080000 /* Host Interface Unit error [5211+] */
+#define AR5K_IMR_BNR 0x00100000 /* Beacon not ready [5211+] */
+#define AR5K_IMR_MCABT 0x00100000 /* Master Cycle Abort [5210] */
+#define AR5K_IMR_RXCHIRP 0x00200000 /* CHIRP Received [5212+]*/
+#define AR5K_IMR_SSERR 0x00200000 /* Signaled System Error [5210] */
+#define AR5K_IMR_DPERR 0x00400000 /* Det par Error (?) [5210] */
+#define AR5K_IMR_RXDOPPLER 0x00400000 /* Doppler chirp received [5212+] */
+#define AR5K_IMR_TIM 0x00800000 /* [5211+] */
+#define AR5K_IMR_BCNMISC 0x00800000 /* 'or' of TIM, CAB_END, DTIM_SYNC, BCN_TIMEOUT,
+ CAB_TIMEOUT and DTIM bits from SISR2 [5212+] */
+#define AR5K_IMR_GPIO 0x01000000 /* GPIO (rf kill)*/
+#define AR5K_IMR_QCBRORN 0x02000000 /* QCU CBR overrun (?) [5211+] */
+#define AR5K_IMR_QCBRURN 0x04000000 /* QCU CBR underrun (?) [5211+] */
+#define AR5K_IMR_QTRIG 0x08000000 /* QCU scheduling trigger [5211+] */
+
+/*
+ * Secondary interrupt mask registers [5211+] (0 - 4)
+ */
+#define AR5K_SIMR0 0x00a4 /* Register Address [5211+] */
+#define AR5K_SIMR0_QCU_TXOK 0x000003ff /* Mask for QCU_TXOK */
+#define AR5K_SIMR0_QCU_TXOK_S 0
+#define AR5K_SIMR0_QCU_TXDESC 0x03ff0000 /* Mask for QCU_TXDESC */
+#define AR5K_SIMR0_QCU_TXDESC_S 16
+
+#define AR5K_SIMR1 0x00a8 /* Register Address [5211+] */
+#define AR5K_SIMR1_QCU_TXERR 0x000003ff /* Mask for QCU_TXERR */
+#define AR5K_SIMR1_QCU_TXERR_S 0
+#define AR5K_SIMR1_QCU_TXEOL 0x03ff0000 /* Mask for QCU_TXEOL */
+#define AR5K_SIMR1_QCU_TXEOL_S 16
+
+#define AR5K_SIMR2 0x00ac /* Register Address [5211+] */
+#define AR5K_SIMR2_QCU_TXURN 0x000003ff /* Mask for QCU_TXURN */
+#define AR5K_SIMR2_QCU_TXURN_S 0
+#define AR5K_SIMR2_MCABT 0x00010000 /* Master Cycle Abort */
+#define AR5K_SIMR2_SSERR 0x00020000 /* Signaled System Error */
+#define AR5K_SIMR2_DPERR 0x00040000 /* Bus parity error */
+#define AR5K_SIMR2_TIM 0x01000000 /* [5212+] */
+#define AR5K_SIMR2_CAB_END 0x02000000 /* [5212+] */
+#define AR5K_SIMR2_DTIM_SYNC 0x04000000 /* DTIM Sync lost [5212+] */
+#define AR5K_SIMR2_BCN_TIMEOUT 0x08000000 /* Beacon Timeout [5212+] */
+#define AR5K_SIMR2_CAB_TIMEOUT 0x10000000 /* CAB Timeout [5212+] */
+#define AR5K_SIMR2_DTIM 0x20000000 /* [5212+] */
+#define AR5K_SIMR2_TSFOOR 0x80000000 /* TSF OOR (?) */
+
+#define AR5K_SIMR3 0x00b0 /* Register Address [5211+] */
+#define AR5K_SIMR3_QCBRORN 0x000003ff /* Mask for QCBRORN */
+#define AR5K_SIMR3_QCBRORN_S 0
+#define AR5K_SIMR3_QCBRURN 0x03ff0000 /* Mask for QCBRURN */
+#define AR5K_SIMR3_QCBRURN_S 16
+
+#define AR5K_SIMR4 0x00b4 /* Register Address [5211+] */
+#define AR5K_SIMR4_QTRIG 0x000003ff /* Mask for QTRIG */
+#define AR5K_SIMR4_QTRIG_S 0
+
+/*
+ * DMA Debug registers 0-7
+ * 0xe0 - 0xfc
+ */
+
+/*
+ * Decompression mask registers [5212+]
+ */
+#define AR5K_DCM_ADDR 0x0400 /*Decompression mask address (index) */
+#define AR5K_DCM_DATA 0x0404 /*Decompression mask data */
+
+/*
+ * Wake On Wireless pattern control register [5212+]
+ */
+#define AR5K_WOW_PCFG 0x0410 /* Register Address */
+#define AR5K_WOW_PCFG_PAT_MATCH_EN 0x00000001 /* Pattern match enable */
+#define AR5K_WOW_PCFG_LONG_FRAME_POL 0x00000002 /* Long frame policy */
+#define AR5K_WOW_PCFG_WOBMISS 0x00000004 /* Wake on bea(con) miss (?) */
+#define AR5K_WOW_PCFG_PAT_0_EN 0x00000100 /* Enable pattern 0 */
+#define AR5K_WOW_PCFG_PAT_1_EN 0x00000200 /* Enable pattern 1 */
+#define AR5K_WOW_PCFG_PAT_2_EN 0x00000400 /* Enable pattern 2 */
+#define AR5K_WOW_PCFG_PAT_3_EN 0x00000800 /* Enable pattern 3 */
+#define AR5K_WOW_PCFG_PAT_4_EN 0x00001000 /* Enable pattern 4 */
+#define AR5K_WOW_PCFG_PAT_5_EN 0x00002000 /* Enable pattern 5 */
+
+/*
+ * Wake On Wireless pattern index register (?) [5212+]
+ */
+#define AR5K_WOW_PAT_IDX 0x0414
+
+/*
+ * Wake On Wireless pattern data register [5212+]
+ */
+#define AR5K_WOW_PAT_DATA 0x0418 /* Register Address */
+#define AR5K_WOW_PAT_DATA_0_3_V 0x00000001 /* Pattern 0, 3 value */
+#define AR5K_WOW_PAT_DATA_1_4_V 0x00000100 /* Pattern 1, 4 value */
+#define AR5K_WOW_PAT_DATA_2_5_V 0x00010000 /* Pattern 2, 5 value */
+#define AR5K_WOW_PAT_DATA_0_3_M 0x01000000 /* Pattern 0, 3 mask */
+#define AR5K_WOW_PAT_DATA_1_4_M 0x04000000 /* Pattern 1, 4 mask */
+#define AR5K_WOW_PAT_DATA_2_5_M 0x10000000 /* Pattern 2, 5 mask */
+
+/*
+ * Decompression configuration registers [5212+]
+ */
+#define AR5K_DCCFG 0x0420 /* Register Address */
+#define AR5K_DCCFG_GLOBAL_EN 0x00000001 /* Enable decompression on all queues */
+#define AR5K_DCCFG_BYPASS_EN 0x00000002 /* Bypass decompression */
+#define AR5K_DCCFG_BCAST_EN 0x00000004 /* Enable decompression for bcast frames */
+#define AR5K_DCCFG_MCAST_EN 0x00000008 /* Enable decompression for mcast frames */
+
+/*
+ * Compression configuration registers [5212+]
+ */
+#define AR5K_CCFG 0x0600 /* Register Address */
+#define AR5K_CCFG_WINDOW_SIZE 0x00000007 /* Compression window size */
+#define AR5K_CCFG_CPC_EN 0x00000008 /* Enable performance counters */
+
+#define AR5K_CCFG_CCU 0x0604 /* Register Address */
+#define AR5K_CCFG_CCU_CUP_EN 0x00000001 /* CCU Catchup enable */
+#define AR5K_CCFG_CCU_CREDIT 0x00000002 /* CCU Credit (field) */
+#define AR5K_CCFG_CCU_CD_THRES 0x00000080 /* CCU Cyc(lic?) debt threshold (field) */
+#define AR5K_CCFG_CCU_CUP_LCNT 0x00010000 /* CCU Catchup lit(?) count */
+#define AR5K_CCFG_CCU_INIT 0x00100200 /* Initial value during reset */
+
+/*
+ * Compression performance counter registers [5212+]
+ */
+#define AR5K_CPC0 0x0610 /* Compression performance counter 0 */
+#define AR5K_CPC1 0x0614 /* Compression performance counter 1*/
+#define AR5K_CPC2 0x0618 /* Compression performance counter 2 */
+#define AR5K_CPC3 0x061c /* Compression performance counter 3 */
+#define AR5K_CPCOVF 0x0620 /* Compression performance overflow */
+
+
+/*
+ * Queue control unit (QCU) registers [5211+]
+ *
+ * Card has 12 TX Queues but i see that only 0-9 are used (?)
+ * both in binary HAL (see ah.h) and ar5k. Each queue has it's own
+ * TXDP at addresses 0x0800 - 0x082c, a CBR (Constant Bit Rate)
+ * configuration register (0x08c0 - 0x08ec), a ready time configuration
+ * register (0x0900 - 0x092c), a misc configuration register (0x09c0 -
+ * 0x09ec) and a status register (0x0a00 - 0x0a2c). We also have some
+ * global registers, QCU transmit enable/disable and "one shot arm (?)"
+ * set/clear, which contain status for all queues (we shift by 1 for each
+ * queue). To access these registers easily we define some macros here
+ * that are used inside HAL. For more infos check out *_tx_queue functs.
+ */
+
+/*
+ * Generic QCU Register access macros
+ */
+#define AR5K_QUEUE_REG(_r, _q) (((_q) << 2) + _r)
+#define AR5K_QCU_GLOBAL_READ(_r, _q) (AR5K_REG_READ(_r) & (1 << _q))
+#define AR5K_QCU_GLOBAL_WRITE(_r, _q) AR5K_REG_WRITE(_r, (1 << _q))
+
+/*
+ * QCU Transmit descriptor pointer registers
+ */
+#define AR5K_QCU_TXDP_BASE 0x0800 /* Register Address - Queue0 TXDP */
+#define AR5K_QUEUE_TXDP(_q) AR5K_QUEUE_REG(AR5K_QCU_TXDP_BASE, _q)
+
+/*
+ * QCU Transmit enable register
+ */
+#define AR5K_QCU_TXE 0x0840
+#define AR5K_ENABLE_QUEUE(_q) AR5K_QCU_GLOBAL_WRITE(AR5K_QCU_TXE, _q)
+#define AR5K_QUEUE_ENABLED(_q) AR5K_QCU_GLOBAL_READ(AR5K_QCU_TXE, _q)
+
+/*
+ * QCU Transmit disable register
+ */
+#define AR5K_QCU_TXD 0x0880
+#define AR5K_DISABLE_QUEUE(_q) AR5K_QCU_GLOBAL_WRITE(AR5K_QCU_TXD, _q)
+#define AR5K_QUEUE_DISABLED(_q) AR5K_QCU_GLOBAL_READ(AR5K_QCU_TXD, _q)
+
+/*
+ * QCU Constant Bit Rate configuration registers
+ */
+#define AR5K_QCU_CBRCFG_BASE 0x08c0 /* Register Address - Queue0 CBRCFG */
+#define AR5K_QCU_CBRCFG_INTVAL 0x00ffffff /* CBR Interval mask */
+#define AR5K_QCU_CBRCFG_INTVAL_S 0
+#define AR5K_QCU_CBRCFG_ORN_THRES 0xff000000 /* CBR overrun threshold mask */
+#define AR5K_QCU_CBRCFG_ORN_THRES_S 24
+#define AR5K_QUEUE_CBRCFG(_q) AR5K_QUEUE_REG(AR5K_QCU_CBRCFG_BASE, _q)
+
+/*
+ * QCU Ready time configuration registers
+ */
+#define AR5K_QCU_RDYTIMECFG_BASE 0x0900 /* Register Address - Queue0 RDYTIMECFG */
+#define AR5K_QCU_RDYTIMECFG_INTVAL 0x00ffffff /* Ready time interval mask */
+#define AR5K_QCU_RDYTIMECFG_INTVAL_S 0
+#define AR5K_QCU_RDYTIMECFG_ENABLE 0x01000000 /* Ready time enable mask */
+#define AR5K_QUEUE_RDYTIMECFG(_q) AR5K_QUEUE_REG(AR5K_QCU_RDYTIMECFG_BASE, _q)
+
+/*
+ * QCU one shot arm set registers
+ */
+#define AR5K_QCU_ONESHOTARM_SET 0x0940 /* Register Address -QCU "one shot arm set (?)" */
+#define AR5K_QCU_ONESHOTARM_SET_M 0x0000ffff
+
+/*
+ * QCU one shot arm clear registers
+ */
+#define AR5K_QCU_ONESHOTARM_CLEAR 0x0980 /* Register Address -QCU "one shot arm clear (?)" */
+#define AR5K_QCU_ONESHOTARM_CLEAR_M 0x0000ffff
+
+/*
+ * QCU misc registers
+ */
+#define AR5K_QCU_MISC_BASE 0x09c0 /* Register Address -Queue0 MISC */
+#define AR5K_QCU_MISC_FRSHED_M 0x0000000f /* Frame scheduling mask */
+#define AR5K_QCU_MISC_FRSHED_ASAP 0 /* ASAP */
+#define AR5K_QCU_MISC_FRSHED_CBR 1 /* Constant Bit Rate */
+#define AR5K_QCU_MISC_FRSHED_DBA_GT 2 /* DMA Beacon alert gated */
+#define AR5K_QCU_MISC_FRSHED_TIM_GT 3 /* TIMT gated */
+#define AR5K_QCU_MISC_FRSHED_BCN_SENT_GT 4 /* Beacon sent gated */
+#define AR5K_QCU_MISC_ONESHOT_ENABLE 0x00000010 /* Oneshot enable */
+#define AR5K_QCU_MISC_CBREXP_DIS 0x00000020 /* Disable CBR expired counter (normal queue) */
+#define AR5K_QCU_MISC_CBREXP_BCN_DIS 0x00000040 /* Disable CBR expired counter (beacon queue) */
+#define AR5K_QCU_MISC_BCN_ENABLE 0x00000080 /* Enable Beacon use */
+#define AR5K_QCU_MISC_CBR_THRES_ENABLE 0x00000100 /* CBR expired threshold enabled */
+#define AR5K_QCU_MISC_RDY_VEOL_POLICY 0x00000200 /* TXE reset when RDYTIME expired or VEOL */
+#define AR5K_QCU_MISC_CBR_RESET_CNT 0x00000400 /* CBR threshold (counter) reset */
+#define AR5K_QCU_MISC_DCU_EARLY 0x00000800 /* DCU early termination */
+#define AR5K_QCU_MISC_DCU_CMP_EN 0x00001000 /* Enable frame compression */
+#define AR5K_QUEUE_MISC(_q) AR5K_QUEUE_REG(AR5K_QCU_MISC_BASE, _q)
+
+
+/*
+ * QCU status registers
+ */
+#define AR5K_QCU_STS_BASE 0x0a00 /* Register Address - Queue0 STS */
+#define AR5K_QCU_STS_FRMPENDCNT 0x00000003 /* Frames pending counter */
+#define AR5K_QCU_STS_CBREXPCNT 0x0000ff00 /* CBR expired counter */
+#define AR5K_QUEUE_STATUS(_q) AR5K_QUEUE_REG(AR5K_QCU_STS_BASE, _q)
+
+/*
+ * QCU ready time shutdown register
+ */
+#define AR5K_QCU_RDYTIMESHDN 0x0a40
+#define AR5K_QCU_RDYTIMESHDN_M 0x000003ff
+
+/*
+ * QCU compression buffer base registers [5212+]
+ */
+#define AR5K_QCU_CBB_SELECT 0x0b00
+#define AR5K_QCU_CBB_ADDR 0x0b04
+#define AR5K_QCU_CBB_ADDR_S 9
+
+/*
+ * QCU compression buffer configuration register [5212+]
+ * (buffer size)
+ */
+#define AR5K_QCU_CBCFG 0x0b08
+
+
+
+/*
+ * Distributed Coordination Function (DCF) control unit (DCU)
+ * registers [5211+]
+ *
+ * These registers control the various characteristics of each queue
+ * for 802.11e (WME) compatibility so they go together with
+ * QCU registers in pairs. For each queue we have a QCU mask register,
+ * (0x1000 - 0x102c), a local-IFS settings register (0x1040 - 0x106c),
+ * a retry limit register (0x1080 - 0x10ac), a channel time register
+ * (0x10c0 - 0x10ec), a misc-settings register (0x1100 - 0x112c) and
+ * a sequence number register (0x1140 - 0x116c). It seems that "global"
+ * registers here affect all queues (see use of DCU_GBL_IFS_SLOT in ar5k).
+ * We use the same macros here for easier register access.
+ *
+ */
+
+/*
+ * DCU QCU mask registers
+ */
+#define AR5K_DCU_QCUMASK_BASE 0x1000 /* Register Address -Queue0 DCU_QCUMASK */
+#define AR5K_DCU_QCUMASK_M 0x000003ff
+#define AR5K_QUEUE_QCUMASK(_q) AR5K_QUEUE_REG(AR5K_DCU_QCUMASK_BASE, _q)
+
+/*
+ * DCU local Inter Frame Space settings register
+ */
+#define AR5K_DCU_LCL_IFS_BASE 0x1040 /* Register Address -Queue0 DCU_LCL_IFS */
+#define AR5K_DCU_LCL_IFS_CW_MIN 0x000003ff /* Minimum Contention Window */
+#define AR5K_DCU_LCL_IFS_CW_MIN_S 0
+#define AR5K_DCU_LCL_IFS_CW_MAX 0x000ffc00 /* Maximum Contention Window */
+#define AR5K_DCU_LCL_IFS_CW_MAX_S 10
+#define AR5K_DCU_LCL_IFS_AIFS 0x0ff00000 /* Arbitrated Interframe Space */
+#define AR5K_DCU_LCL_IFS_AIFS_S 20
+#define AR5K_DCU_LCL_IFS_AIFS_MAX 0xfc /* Anything above that can cause DCU to hang */
+#define AR5K_QUEUE_DFS_LOCAL_IFS(_q) AR5K_QUEUE_REG(AR5K_DCU_LCL_IFS_BASE, _q)
+
+/*
+ * DCU retry limit registers
+ * all these fields don't allow zero values
+ */
+#define AR5K_DCU_RETRY_LMT_BASE 0x1080 /* Register Address -Queue0 DCU_RETRY_LMT */
+#define AR5K_DCU_RETRY_LMT_RTS 0x0000000f /* RTS failure limit. Transmission fails if no CTS is received for this number of times */
+#define AR5K_DCU_RETRY_LMT_RTS_S 0
+#define AR5K_DCU_RETRY_LMT_STA_RTS 0x00003f00 /* STA RTS failure limit. If exceeded CW reset */
+#define AR5K_DCU_RETRY_LMT_STA_RTS_S 8
+#define AR5K_DCU_RETRY_LMT_STA_DATA 0x000fc000 /* STA data failure limit. If exceeded CW reset. */
+#define AR5K_DCU_RETRY_LMT_STA_DATA_S 14
+#define AR5K_QUEUE_DFS_RETRY_LIMIT(_q) AR5K_QUEUE_REG(AR5K_DCU_RETRY_LMT_BASE, _q)
+
+/*
+ * DCU channel time registers
+ */
+#define AR5K_DCU_CHAN_TIME_BASE 0x10c0 /* Register Address -Queue0 DCU_CHAN_TIME */
+#define AR5K_DCU_CHAN_TIME_DUR 0x000fffff /* Channel time duration */
+#define AR5K_DCU_CHAN_TIME_DUR_S 0
+#define AR5K_DCU_CHAN_TIME_ENABLE 0x00100000 /* Enable channel time */
+#define AR5K_QUEUE_DFS_CHANNEL_TIME(_q) AR5K_QUEUE_REG(AR5K_DCU_CHAN_TIME_BASE, _q)
+
+/*
+ * DCU misc registers [5211+]
+ *
+ * Note: Arbiter lockout control controls the
+ * behaviour on low priority queues when we have multiple queues
+ * with pending frames. Intra-frame lockout means we wait until
+ * the queue's current frame transmits (with post frame backoff and bursting)
+ * before we transmit anything else and global lockout means we
+ * wait for the whole queue to finish before higher priority queues
+ * can transmit (this is used on beacon and CAB queues).
+ * No lockout means there is no special handling.
+ */
+#define AR5K_DCU_MISC_BASE 0x1100 /* Register Address -Queue0 DCU_MISC */
+#define AR5K_DCU_MISC_BACKOFF 0x0000003f /* Mask for backoff threshold */
+#define AR5K_DCU_MISC_ETS_RTS_POL 0x00000040 /* End of transmission series
+ station RTS/data failure count
+ reset policy (?) */
+#define AR5K_DCU_MISC_ETS_CW_POL 0x00000080 /* End of transmission series
+ CW reset policy */
+#define AR5K_DCU_MISC_FRAG_WAIT 0x00000100 /* Wait for next fragment */
+#define AR5K_DCU_MISC_BACKOFF_FRAG 0x00000200 /* Enable backoff while bursting */
+#define AR5K_DCU_MISC_HCFPOLL_ENABLE 0x00000800 /* CF - Poll enable */
+#define AR5K_DCU_MISC_BACKOFF_PERSIST 0x00001000 /* Persistent backoff */
+#define AR5K_DCU_MISC_FRMPRFTCH_ENABLE 0x00002000 /* Enable frame pre-fetch */
+#define AR5K_DCU_MISC_VIRTCOL 0x0000c000 /* Mask for Virtual Collision (?) */
+#define AR5K_DCU_MISC_VIRTCOL_NORMAL 0
+#define AR5K_DCU_MISC_VIRTCOL_IGNORE 1
+#define AR5K_DCU_MISC_BCN_ENABLE 0x00010000 /* Enable Beacon use */
+#define AR5K_DCU_MISC_ARBLOCK_CTL 0x00060000 /* Arbiter lockout control mask */
+#define AR5K_DCU_MISC_ARBLOCK_CTL_S 17
+#define AR5K_DCU_MISC_ARBLOCK_CTL_NONE 0 /* No arbiter lockout */
+#define AR5K_DCU_MISC_ARBLOCK_CTL_INTFRM 1 /* Intra-frame lockout */
+#define AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL 2 /* Global lockout */
+#define AR5K_DCU_MISC_ARBLOCK_IGNORE 0x00080000 /* Ignore Arbiter lockout */
+#define AR5K_DCU_MISC_SEQ_NUM_INCR_DIS 0x00100000 /* Disable sequence number increment */
+#define AR5K_DCU_MISC_POST_FR_BKOFF_DIS 0x00200000 /* Disable post-frame backoff */
+#define AR5K_DCU_MISC_VIRT_COLL_POLICY 0x00400000 /* Virtual Collision cw policy */
+#define AR5K_DCU_MISC_BLOWN_IFS_POLICY 0x00800000 /* Blown IFS policy (?) */
+#define AR5K_DCU_MISC_SEQNUM_CTL 0x01000000 /* Sequence number control (?) */
+#define AR5K_QUEUE_DFS_MISC(_q) AR5K_QUEUE_REG(AR5K_DCU_MISC_BASE, _q)
+
+/*
+ * DCU frame sequence number registers
+ */
+#define AR5K_DCU_SEQNUM_BASE 0x1140
+#define AR5K_DCU_SEQNUM_M 0x00000fff
+#define AR5K_QUEUE_DCU_SEQNUM(_q) AR5K_QUEUE_REG(AR5K_DCU_SEQNUM_BASE, _q)
+
+/*
+ * DCU global IFS SIFS register
+ */
+#define AR5K_DCU_GBL_IFS_SIFS 0x1030
+#define AR5K_DCU_GBL_IFS_SIFS_M 0x0000ffff
+
+/*
+ * DCU global IFS slot interval register
+ */
+#define AR5K_DCU_GBL_IFS_SLOT 0x1070
+#define AR5K_DCU_GBL_IFS_SLOT_M 0x0000ffff
+
+/*
+ * DCU global IFS EIFS register
+ */
+#define AR5K_DCU_GBL_IFS_EIFS 0x10b0
+#define AR5K_DCU_GBL_IFS_EIFS_M 0x0000ffff
+
+/*
+ * DCU global IFS misc register
+ *
+ * LFSR stands for Linear Feedback Shift Register
+ * and it's used for generating pseudo-random
+ * number sequences.
+ *
+ * (If i understand correctly, random numbers are
+ * used for idle sensing -multiplied with cwmin/max etc-)
+ */
+#define AR5K_DCU_GBL_IFS_MISC 0x10f0 /* Register Address */
+#define AR5K_DCU_GBL_IFS_MISC_LFSR_SLICE 0x00000007 /* LFSR Slice Select */
+#define AR5K_DCU_GBL_IFS_MISC_TURBO_MODE 0x00000008 /* Turbo mode */
+#define AR5K_DCU_GBL_IFS_MISC_SIFS_DUR_USEC 0x000003f0 /* SIFS Duration mask */
+#define AR5K_DCU_GBL_IFS_MISC_SIFS_DUR_USEC_S 4
+#define AR5K_DCU_GBL_IFS_MISC_USEC_DUR 0x000ffc00 /* USEC Duration mask */
+#define AR5K_DCU_GBL_IFS_MISC_USEC_DUR_S 10
+#define AR5K_DCU_GBL_IFS_MISC_DCU_ARB_DELAY 0x00300000 /* DCU Arbiter delay mask */
+#define AR5K_DCU_GBL_IFS_MISC_SIFS_CNT_RST 0x00400000 /* SIFS cnt reset policy (?) */
+#define AR5K_DCU_GBL_IFS_MISC_AIFS_CNT_RST 0x00800000 /* AIFS cnt reset policy (?) */
+#define AR5K_DCU_GBL_IFS_MISC_RND_LFSR_SL_DIS 0x01000000 /* Disable random LFSR slice */
+
+/*
+ * DCU frame prefetch control register
+ */
+#define AR5K_DCU_FP 0x1230 /* Register Address */
+#define AR5K_DCU_FP_NOBURST_DCU_EN 0x00000001 /* Enable non-burst prefetch on DCU (?) */
+#define AR5K_DCU_FP_NOBURST_EN 0x00000010 /* Enable non-burst prefetch (?) */
+#define AR5K_DCU_FP_BURST_DCU_EN 0x00000020 /* Enable burst prefetch on DCU (?) */
+
+/*
+ * DCU transmit pause control/status register
+ */
+#define AR5K_DCU_TXP 0x1270 /* Register Address */
+#define AR5K_DCU_TXP_M 0x000003ff /* Tx pause mask */
+#define AR5K_DCU_TXP_STATUS 0x00010000 /* Tx pause status */
+
+/*
+ * DCU transmit filter table 0 (32 entries)
+ * each entry contains a 32bit slice of the
+ * 128bit tx filter for each DCU (4 slices per DCU)
+ */
+#define AR5K_DCU_TX_FILTER_0_BASE 0x1038
+#define AR5K_DCU_TX_FILTER_0(_n) (AR5K_DCU_TX_FILTER_0_BASE + (_n * 64))
+
+/*
+ * DCU transmit filter table 1 (16 entries)
+ */
+#define AR5K_DCU_TX_FILTER_1_BASE 0x103c
+#define AR5K_DCU_TX_FILTER_1(_n) (AR5K_DCU_TX_FILTER_1_BASE + (_n * 64))
+
+/*
+ * DCU clear transmit filter register
+ */
+#define AR5K_DCU_TX_FILTER_CLR 0x143c
+
+/*
+ * DCU set transmit filter register
+ */
+#define AR5K_DCU_TX_FILTER_SET 0x147c
+
+/*
+ * Reset control register
+ */
+#define AR5K_RESET_CTL 0x4000 /* Register Address */
+#define AR5K_RESET_CTL_PCU 0x00000001 /* Protocol Control Unit reset */
+#define AR5K_RESET_CTL_DMA 0x00000002 /* DMA (Rx/Tx) reset [5210] */
+#define AR5K_RESET_CTL_BASEBAND 0x00000002 /* Baseband reset [5211+] */
+#define AR5K_RESET_CTL_MAC 0x00000004 /* MAC reset (PCU+Baseband ?) [5210] */
+#define AR5K_RESET_CTL_PHY 0x00000008 /* PHY reset [5210] */
+#define AR5K_RESET_CTL_PCI 0x00000010 /* PCI Core reset (interrupts etc) */
+
+/*
+ * Sleep control register
+ */
+#define AR5K_SLEEP_CTL 0x4004 /* Register Address */
+#define AR5K_SLEEP_CTL_SLDUR 0x0000ffff /* Sleep duration mask */
+#define AR5K_SLEEP_CTL_SLDUR_S 0
+#define AR5K_SLEEP_CTL_SLE 0x00030000 /* Sleep enable mask */
+#define AR5K_SLEEP_CTL_SLE_S 16
+#define AR5K_SLEEP_CTL_SLE_WAKE 0x00000000 /* Force chip awake */
+#define AR5K_SLEEP_CTL_SLE_SLP 0x00010000 /* Force chip sleep */
+#define AR5K_SLEEP_CTL_SLE_ALLOW 0x00020000 /* Normal sleep policy */
+#define AR5K_SLEEP_CTL_SLE_UNITS 0x00000008 /* [5211+] */
+#define AR5K_SLEEP_CTL_DUR_TIM_POL 0x00040000 /* Sleep duration timing policy */
+#define AR5K_SLEEP_CTL_DUR_WRITE_POL 0x00080000 /* Sleep duration write policy */
+#define AR5K_SLEEP_CTL_SLE_POL 0x00100000 /* Sleep policy mode */
+
+/*
+ * Interrupt pending register
+ */
+#define AR5K_INTPEND 0x4008
+#define AR5K_INTPEND_M 0x00000001
+
+/*
+ * Sleep force register
+ */
+#define AR5K_SFR 0x400c
+#define AR5K_SFR_EN 0x00000001
+
+/*
+ * PCI configuration register
+ * TODO: Fix LED stuff
+ */
+#define AR5K_PCICFG 0x4010 /* Register Address */
+#define AR5K_PCICFG_EEAE 0x00000001 /* Eeprom access enable [5210] */
+#define AR5K_PCICFG_SLEEP_CLOCK_EN 0x00000002 /* Enable sleep clock */
+#define AR5K_PCICFG_CLKRUNEN 0x00000004 /* CLKRUN enable [5211+] */
+#define AR5K_PCICFG_EESIZE 0x00000018 /* Mask for EEPROM size [5211+] */
+#define AR5K_PCICFG_EESIZE_S 3
+#define AR5K_PCICFG_EESIZE_4K 0 /* 4K */
+#define AR5K_PCICFG_EESIZE_8K 1 /* 8K */
+#define AR5K_PCICFG_EESIZE_16K 2 /* 16K */
+#define AR5K_PCICFG_EESIZE_FAIL 3 /* Failed to get size [5211+] */
+#define AR5K_PCICFG_LED 0x00000060 /* Led status [5211+] */
+#define AR5K_PCICFG_LED_NONE 0x00000000 /* Default [5211+] */
+#define AR5K_PCICFG_LED_PEND 0x00000020 /* Scan / Auth pending */
+#define AR5K_PCICFG_LED_ASSOC 0x00000040 /* Associated */
+#define AR5K_PCICFG_BUS_SEL 0x00000380 /* Mask for "bus select" [5211+] (?) */
+#define AR5K_PCICFG_CBEFIX_DIS 0x00000400 /* Disable CBE fix */
+#define AR5K_PCICFG_SL_INTEN 0x00000800 /* Enable interrupts when asleep */
+#define AR5K_PCICFG_LED_BCTL 0x00001000 /* Led blink (?) [5210] */
+#define AR5K_PCICFG_RETRY_FIX 0x00001000 /* Enable pci core retry fix */
+#define AR5K_PCICFG_SL_INPEN 0x00002000 /* Sleep even with pending interrupts*/
+#define AR5K_PCICFG_SPWR_DN 0x00010000 /* Mask for power status */
+#define AR5K_PCICFG_LEDMODE 0x000e0000 /* Ledmode [5211+] */
+#define AR5K_PCICFG_LEDMODE_PROP 0x00000000 /* Blink on standard traffic [5211+] */
+#define AR5K_PCICFG_LEDMODE_PROM 0x00020000 /* Default mode (blink on any traffic) [5211+] */
+#define AR5K_PCICFG_LEDMODE_PWR 0x00040000 /* Some other blinking mode (?) [5211+] */
+#define AR5K_PCICFG_LEDMODE_RAND 0x00060000 /* Random blinking (?) [5211+] */
+#define AR5K_PCICFG_LEDBLINK 0x00700000 /* Led blink rate */
+#define AR5K_PCICFG_LEDBLINK_S 20
+#define AR5K_PCICFG_LEDSLOW 0x00800000 /* Slowest led blink rate [5211+] */
+#define AR5K_PCICFG_LEDSTATE \
+ (AR5K_PCICFG_LED | AR5K_PCICFG_LEDMODE | \
+ AR5K_PCICFG_LEDBLINK | AR5K_PCICFG_LEDSLOW)
+#define AR5K_PCICFG_SLEEP_CLOCK_RATE 0x03000000 /* Sleep clock rate */
+#define AR5K_PCICFG_SLEEP_CLOCK_RATE_S 24
+
+/*
+ * "General Purpose Input/Output" (GPIO) control register
+ *
+ * I'm not sure about this but after looking at the code
+ * for all chipsets here is what i got.
+ *
+ * We have 6 GPIOs (pins), each GPIO has 4 modes (2 bits)
+ * Mode 0 -> always input
+ * Mode 1 -> output when GPIODO for this GPIO is set to 0
+ * Mode 2 -> output when GPIODO for this GPIO is set to 1
+ * Mode 3 -> always output
+ *
+ * For more infos check out get_gpio/set_gpio and
+ * set_gpio_input/set_gpio_output functs.
+ * For more infos on gpio interrupt check out set_gpio_intr.
+ */
+#define AR5K_NUM_GPIO 6
+
+#define AR5K_GPIOCR 0x4014 /* Register Address */
+#define AR5K_GPIOCR_INT_ENA 0x00008000 /* Enable GPIO interrupt */
+#define AR5K_GPIOCR_INT_SELL 0x00000000 /* Generate interrupt when pin is low */
+#define AR5K_GPIOCR_INT_SELH 0x00010000 /* Generate interrupt when pin is high */
+#define AR5K_GPIOCR_IN(n) (0 << ((n) * 2)) /* Mode 0 for pin n */
+#define AR5K_GPIOCR_OUT0(n) (1 << ((n) * 2)) /* Mode 1 for pin n */
+#define AR5K_GPIOCR_OUT1(n) (2 << ((n) * 2)) /* Mode 2 for pin n */
+#define AR5K_GPIOCR_OUT(n) (3 << ((n) * 2)) /* Mode 3 for pin n */
+#define AR5K_GPIOCR_INT_SEL(n) ((n) << 12) /* Interrupt for GPIO pin n */
+
+/*
+ * "General Purpose Input/Output" (GPIO) data output register
+ */
+#define AR5K_GPIODO 0x4018
+
+/*
+ * "General Purpose Input/Output" (GPIO) data input register
+ */
+#define AR5K_GPIODI 0x401c
+#define AR5K_GPIODI_M 0x0000002f
+
+/*
+ * Silicon revision register
+ */
+#define AR5K_SREV 0x4020 /* Register Address */
+#define AR5K_SREV_REV 0x0000000f /* Mask for revision */
+#define AR5K_SREV_REV_S 0
+#define AR5K_SREV_VER 0x000000ff /* Mask for version */
+#define AR5K_SREV_VER_S 4
+
+/*
+ * TXE write posting register
+ */
+#define AR5K_TXEPOST 0x4028
+
+/*
+ * QCU sleep mask
+ */
+#define AR5K_QCU_SLEEP_MASK 0x402c
+
+/* 0x4068 is compression buffer configuration
+ * register on 5414 and pm configuration register
+ * on 5424 and newer pci-e chips. */
+
+/*
+ * Compression buffer configuration
+ * register (enable/disable) [5414]
+ */
+#define AR5K_5414_CBCFG 0x4068
+#define AR5K_5414_CBCFG_BUF_DIS 0x10 /* Disable buffer */
+
+/*
+ * PCI-E Power management configuration
+ * and status register [5424+]
+ */
+#define AR5K_PCIE_PM_CTL 0x4068 /* Register address */
+/* Only 5424 */
+#define AR5K_PCIE_PM_CTL_L1_WHEN_D2 0x00000001 /* enable PCIe core enter L1
+ when d2_sleep_en is asserted */
+#define AR5K_PCIE_PM_CTL_L0_L0S_CLEAR 0x00000002 /* Clear L0 and L0S counters */
+#define AR5K_PCIE_PM_CTL_L0_L0S_EN 0x00000004 /* Start L0 nd L0S counters */
+#define AR5K_PCIE_PM_CTL_LDRESET_EN 0x00000008 /* Enable reset when link goes
+ down */
+/* Wake On Wireless */
+#define AR5K_PCIE_PM_CTL_PME_EN 0x00000010 /* PME Enable */
+#define AR5K_PCIE_PM_CTL_AUX_PWR_DET 0x00000020 /* Aux power detect */
+#define AR5K_PCIE_PM_CTL_PME_CLEAR 0x00000040 /* Clear PME */
+#define AR5K_PCIE_PM_CTL_PSM_D0 0x00000080
+#define AR5K_PCIE_PM_CTL_PSM_D1 0x00000100
+#define AR5K_PCIE_PM_CTL_PSM_D2 0x00000200
+#define AR5K_PCIE_PM_CTL_PSM_D3 0x00000400
+
+/*
+ * PCI-E Workaround enable register
+ */
+#define AR5K_PCIE_WAEN 0x407c
+
+/*
+ * PCI-E Serializer/Deserializer
+ * registers
+ */
+#define AR5K_PCIE_SERDES 0x4080
+#define AR5K_PCIE_SERDES_RESET 0x4084
+
+/*====EEPROM REGISTERS====*/
+
+/*
+ * EEPROM access registers
+ *
+ * Here we got a difference between 5210/5211-12
+ * read data register for 5210 is at 0x6800 and
+ * status register is at 0x6c00. There is also
+ * no eeprom command register on 5210 and the
+ * offsets are different.
+ *
+ * To read eeprom data for a specific offset:
+ * 5210 - enable eeprom access (AR5K_PCICFG_EEAE)
+ * read AR5K_EEPROM_BASE +(4 * offset)
+ * check the eeprom status register
+ * and read eeprom data register.
+ *
+ * 5211 - write offset to AR5K_EEPROM_BASE
+ * 5212 write AR5K_EEPROM_CMD_READ on AR5K_EEPROM_CMD
+ * check the eeprom status register
+ * and read eeprom data register.
+ *
+ * To write eeprom data for a specific offset:
+ * 5210 - enable eeprom access (AR5K_PCICFG_EEAE)
+ * write data to AR5K_EEPROM_BASE +(4 * offset)
+ * check the eeprom status register
+ * 5211 - write AR5K_EEPROM_CMD_RESET on AR5K_EEPROM_CMD
+ * 5212 write offset to AR5K_EEPROM_BASE
+ * write data to data register
+ * write AR5K_EEPROM_CMD_WRITE on AR5K_EEPROM_CMD
+ * check the eeprom status register
+ *
+ * For more infos check eeprom_* functs and the ar5k.c
+ * file posted in madwifi-devel mailing list.
+ * http://sourceforge.net/mailarchive/message.php?msg_id=8966525
+ *
+ */
+#define AR5K_EEPROM_BASE 0x6000
+
+/*
+ * EEPROM data register
+ */
+#define AR5K_EEPROM_DATA_5211 0x6004
+#define AR5K_EEPROM_DATA_5210 0x6800
+#define AR5K_EEPROM_DATA (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_EEPROM_DATA_5210 : AR5K_EEPROM_DATA_5211)
+
+/*
+ * EEPROM command register
+ */
+#define AR5K_EEPROM_CMD 0x6008 /* Register Address */
+#define AR5K_EEPROM_CMD_READ 0x00000001 /* EEPROM read */
+#define AR5K_EEPROM_CMD_WRITE 0x00000002 /* EEPROM write */
+#define AR5K_EEPROM_CMD_RESET 0x00000004 /* EEPROM reset */
+
+/*
+ * EEPROM status register
+ */
+#define AR5K_EEPROM_STAT_5210 0x6c00 /* Register Address [5210] */
+#define AR5K_EEPROM_STAT_5211 0x600c /* Register Address [5211+] */
+#define AR5K_EEPROM_STATUS (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_EEPROM_STAT_5210 : AR5K_EEPROM_STAT_5211)
+#define AR5K_EEPROM_STAT_RDERR 0x00000001 /* EEPROM read failed */
+#define AR5K_EEPROM_STAT_RDDONE 0x00000002 /* EEPROM read successful */
+#define AR5K_EEPROM_STAT_WRERR 0x00000004 /* EEPROM write failed */
+#define AR5K_EEPROM_STAT_WRDONE 0x00000008 /* EEPROM write successful */
+
+/*
+ * EEPROM config register
+ */
+#define AR5K_EEPROM_CFG 0x6010 /* Register Address */
+#define AR5K_EEPROM_CFG_SIZE 0x00000003 /* Size determination override */
+#define AR5K_EEPROM_CFG_SIZE_AUTO 0
+#define AR5K_EEPROM_CFG_SIZE_4KBIT 1
+#define AR5K_EEPROM_CFG_SIZE_8KBIT 2
+#define AR5K_EEPROM_CFG_SIZE_16KBIT 3
+#define AR5K_EEPROM_CFG_WR_WAIT_DIS 0x00000004 /* Disable write wait */
+#define AR5K_EEPROM_CFG_CLK_RATE 0x00000018 /* Clock rate */
+#define AR5K_EEPROM_CFG_CLK_RATE_S 3
+#define AR5K_EEPROM_CFG_CLK_RATE_156KHZ 0
+#define AR5K_EEPROM_CFG_CLK_RATE_312KHZ 1
+#define AR5K_EEPROM_CFG_CLK_RATE_625KHZ 2
+#define AR5K_EEPROM_CFG_PROT_KEY 0x00ffff00 /* Protection key */
+#define AR5K_EEPROM_CFG_PROT_KEY_S 8
+#define AR5K_EEPROM_CFG_LIND_EN 0x01000000 /* Enable length indicator (?) */
+
+
+/*
+ * TODO: Wake On Wireless registers
+ * Range 0x7000 - 0x7ce0
+ */
+
+/*
+ * Protocol Control Unit (PCU) registers
+ */
+/*
+ * Used for checking initial register writes
+ * during channel reset (see reset func)
+ */
+#define AR5K_PCU_MIN 0x8000
+#define AR5K_PCU_MAX 0x8fff
+
+/*
+ * First station id register (Lower 32 bits of MAC address)
+ */
+#define AR5K_STA_ID0 0x8000
+#define AR5K_STA_ID0_ARRD_L32 0xffffffff
+
+/*
+ * Second station id register (Upper 16 bits of MAC address + PCU settings)
+ */
+#define AR5K_STA_ID1 0x8004 /* Register Address */
+#define AR5K_STA_ID1_ADDR_U16 0x0000ffff /* Upper 16 bits of MAC address */
+#define AR5K_STA_ID1_AP 0x00010000 /* Set AP mode */
+#define AR5K_STA_ID1_ADHOC 0x00020000 /* Set Ad-Hoc mode */
+#define AR5K_STA_ID1_PWR_SV 0x00040000 /* Power save reporting */
+#define AR5K_STA_ID1_NO_KEYSRCH 0x00080000 /* No key search */
+#define AR5K_STA_ID1_NO_PSPOLL 0x00100000 /* No power save polling [5210] */
+#define AR5K_STA_ID1_PCF_5211 0x00100000 /* Enable PCF on [5211+] */
+#define AR5K_STA_ID1_PCF_5210 0x00200000 /* Enable PCF on [5210]*/
+#define AR5K_STA_ID1_PCF (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_STA_ID1_PCF_5210 : AR5K_STA_ID1_PCF_5211)
+#define AR5K_STA_ID1_DEFAULT_ANTENNA 0x00200000 /* Use default antenna */
+#define AR5K_STA_ID1_DESC_ANTENNA 0x00400000 /* Update antenna from descriptor */
+#define AR5K_STA_ID1_RTS_DEF_ANTENNA 0x00800000 /* Use default antenna for RTS */
+#define AR5K_STA_ID1_ACKCTS_6MB 0x01000000 /* Rate to use for ACK/CTS. 0: highest mandatory rate <= RX rate; 1: 1Mbps in B mode */
+#define AR5K_STA_ID1_BASE_RATE_11B 0x02000000 /* 802.11b base rate. 0: 1, 2, 5.5 and 11Mbps; 1: 1 and 2Mbps. [5211+] */
+#define AR5K_STA_ID1_SELFGEN_DEF_ANT 0x04000000 /* Use def. antenna for self generated frames */
+#define AR5K_STA_ID1_CRYPT_MIC_EN 0x08000000 /* Enable MIC */
+#define AR5K_STA_ID1_KEYSRCH_MODE 0x10000000 /* Look up key when key id != 0 */
+#define AR5K_STA_ID1_PRESERVE_SEQ_NUM 0x20000000 /* Preserve sequence number */
+#define AR5K_STA_ID1_CBCIV_ENDIAN 0x40000000 /* ??? */
+#define AR5K_STA_ID1_KEYSRCH_MCAST 0x80000000 /* Do key cache search for mcast frames */
+
+#define AR5K_STA_ID1_ANTENNA_SETTINGS (AR5K_STA_ID1_DEFAULT_ANTENNA | \
+ AR5K_STA_ID1_DESC_ANTENNA | \
+ AR5K_STA_ID1_RTS_DEF_ANTENNA | \
+ AR5K_STA_ID1_SELFGEN_DEF_ANT)
+
+/*
+ * First BSSID register (MAC address, lower 32bits)
+ */
+#define AR5K_BSS_ID0 0x8008
+
+/*
+ * Second BSSID register (MAC address in upper 16 bits)
+ *
+ * AID: Association ID
+ */
+#define AR5K_BSS_ID1 0x800c
+#define AR5K_BSS_ID1_AID 0xffff0000
+#define AR5K_BSS_ID1_AID_S 16
+
+/*
+ * Backoff slot time register
+ */
+#define AR5K_SLOT_TIME 0x8010
+
+/*
+ * ACK/CTS timeout register
+ */
+#define AR5K_TIME_OUT 0x8014 /* Register Address */
+#define AR5K_TIME_OUT_ACK 0x00001fff /* ACK timeout mask */
+#define AR5K_TIME_OUT_ACK_S 0
+#define AR5K_TIME_OUT_CTS 0x1fff0000 /* CTS timeout mask */
+#define AR5K_TIME_OUT_CTS_S 16
+
+/*
+ * RSSI threshold register
+ */
+#define AR5K_RSSI_THR 0x8018 /* Register Address */
+#define AR5K_RSSI_THR_M 0x000000ff /* Mask for RSSI threshold [5211+] */
+#define AR5K_RSSI_THR_BMISS_5210 0x00000700 /* Mask for Beacon Missed threshold [5210] */
+#define AR5K_RSSI_THR_BMISS_5210_S 8
+#define AR5K_RSSI_THR_BMISS_5211 0x0000ff00 /* Mask for Beacon Missed threshold [5211+] */
+#define AR5K_RSSI_THR_BMISS_5211_S 8
+#define AR5K_RSSI_THR_BMISS (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_RSSI_THR_BMISS_5210 : AR5K_RSSI_THR_BMISS_5211)
+#define AR5K_RSSI_THR_BMISS_S 8
+
+/*
+ * 5210 has more PCU registers because there is no QCU/DCU
+ * so queue parameters are set here, this way a lot common
+ * registers have different address for 5210. To make things
+ * easier we define a macro based on ah->ah_version for common
+ * registers with different addresses and common flags.
+ */
+
+/*
+ * Retry limit register
+ *
+ * Retry limit register for 5210 (no QCU/DCU so it's done in PCU)
+ */
+#define AR5K_NODCU_RETRY_LMT 0x801c /* Register Address */
+#define AR5K_NODCU_RETRY_LMT_SH_RETRY 0x0000000f /* Short retry limit mask */
+#define AR5K_NODCU_RETRY_LMT_SH_RETRY_S 0
+#define AR5K_NODCU_RETRY_LMT_LG_RETRY 0x000000f0 /* Long retry mask */
+#define AR5K_NODCU_RETRY_LMT_LG_RETRY_S 4
+#define AR5K_NODCU_RETRY_LMT_SSH_RETRY 0x00003f00 /* Station short retry limit mask */
+#define AR5K_NODCU_RETRY_LMT_SSH_RETRY_S 8
+#define AR5K_NODCU_RETRY_LMT_SLG_RETRY 0x000fc000 /* Station long retry limit mask */
+#define AR5K_NODCU_RETRY_LMT_SLG_RETRY_S 14
+#define AR5K_NODCU_RETRY_LMT_CW_MIN 0x3ff00000 /* Minimum contention window mask */
+#define AR5K_NODCU_RETRY_LMT_CW_MIN_S 20
+
+/*
+ * Transmit latency register
+ */
+#define AR5K_USEC_5210 0x8020 /* Register Address [5210] */
+#define AR5K_USEC_5211 0x801c /* Register Address [5211+] */
+#define AR5K_USEC (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_USEC_5210 : AR5K_USEC_5211)
+#define AR5K_USEC_1 0x0000007f /* clock cycles for 1us */
+#define AR5K_USEC_1_S 0
+#define AR5K_USEC_32 0x00003f80 /* clock cycles for 1us while on 32MHz clock */
+#define AR5K_USEC_32_S 7
+#define AR5K_USEC_TX_LATENCY_5211 0x007fc000
+#define AR5K_USEC_TX_LATENCY_5211_S 14
+#define AR5K_USEC_RX_LATENCY_5211 0x1f800000
+#define AR5K_USEC_RX_LATENCY_5211_S 23
+#define AR5K_USEC_TX_LATENCY_5210 0x000fc000 /* also for 5311 */
+#define AR5K_USEC_TX_LATENCY_5210_S 14
+#define AR5K_USEC_RX_LATENCY_5210 0x03f00000 /* also for 5311 */
+#define AR5K_USEC_RX_LATENCY_5210_S 20
+
+/*
+ * PCU beacon control register
+ */
+#define AR5K_BEACON_5210 0x8024 /*Register Address [5210] */
+#define AR5K_BEACON_5211 0x8020 /*Register Address [5211+] */
+#define AR5K_BEACON (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_BEACON_5210 : AR5K_BEACON_5211)
+#define AR5K_BEACON_PERIOD 0x0000ffff /* Mask for beacon period */
+#define AR5K_BEACON_PERIOD_S 0
+#define AR5K_BEACON_TIM 0x007f0000 /* Mask for TIM offset */
+#define AR5K_BEACON_TIM_S 16
+#define AR5K_BEACON_ENABLE 0x00800000 /* Enable beacons */
+#define AR5K_BEACON_RESET_TSF 0x01000000 /* Force TSF reset */
+
+/*
+ * CFP period register
+ */
+#define AR5K_CFP_PERIOD_5210 0x8028
+#define AR5K_CFP_PERIOD_5211 0x8024
+#define AR5K_CFP_PERIOD (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_CFP_PERIOD_5210 : AR5K_CFP_PERIOD_5211)
+
+/*
+ * Next beacon time register
+ */
+#define AR5K_TIMER0_5210 0x802c
+#define AR5K_TIMER0_5211 0x8028
+#define AR5K_TIMER0 (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_TIMER0_5210 : AR5K_TIMER0_5211)
+
+/*
+ * Next DMA beacon alert register
+ */
+#define AR5K_TIMER1_5210 0x8030
+#define AR5K_TIMER1_5211 0x802c
+#define AR5K_TIMER1 (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_TIMER1_5210 : AR5K_TIMER1_5211)
+
+/*
+ * Next software beacon alert register
+ */
+#define AR5K_TIMER2_5210 0x8034
+#define AR5K_TIMER2_5211 0x8030
+#define AR5K_TIMER2 (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_TIMER2_5210 : AR5K_TIMER2_5211)
+
+/*
+ * Next ATIM window time register
+ */
+#define AR5K_TIMER3_5210 0x8038
+#define AR5K_TIMER3_5211 0x8034
+#define AR5K_TIMER3 (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_TIMER3_5210 : AR5K_TIMER3_5211)
+
+
+/*
+ * 5210 First inter frame spacing register (IFS)
+ */
+#define AR5K_IFS0 0x8040
+#define AR5K_IFS0_SIFS 0x000007ff
+#define AR5K_IFS0_SIFS_S 0
+#define AR5K_IFS0_DIFS 0x007ff800
+#define AR5K_IFS0_DIFS_S 11
+
+/*
+ * 5210 Second inter frame spacing register (IFS)
+ */
+#define AR5K_IFS1 0x8044
+#define AR5K_IFS1_PIFS 0x00000fff
+#define AR5K_IFS1_PIFS_S 0
+#define AR5K_IFS1_EIFS 0x03fff000
+#define AR5K_IFS1_EIFS_S 12
+#define AR5K_IFS1_CS_EN 0x04000000
+#define AR5K_IFS1_CS_EN_S 26
+
+/*
+ * CFP duration register
+ */
+#define AR5K_CFP_DUR_5210 0x8048
+#define AR5K_CFP_DUR_5211 0x8038
+#define AR5K_CFP_DUR (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_CFP_DUR_5210 : AR5K_CFP_DUR_5211)
+
+/*
+ * Receive filter register
+ */
+#define AR5K_RX_FILTER_5210 0x804c /* Register Address [5210] */
+#define AR5K_RX_FILTER_5211 0x803c /* Register Address [5211+] */
+#define AR5K_RX_FILTER (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_RX_FILTER_5210 : AR5K_RX_FILTER_5211)
+#define AR5K_RX_FILTER_UCAST 0x00000001 /* Don't filter unicast frames */
+#define AR5K_RX_FILTER_MCAST 0x00000002 /* Don't filter multicast frames */
+#define AR5K_RX_FILTER_BCAST 0x00000004 /* Don't filter broadcast frames */
+#define AR5K_RX_FILTER_CONTROL 0x00000008 /* Don't filter control frames */
+#define AR5K_RX_FILTER_BEACON 0x00000010 /* Don't filter beacon frames */
+#define AR5K_RX_FILTER_PROM 0x00000020 /* Set promiscuous mode */
+#define AR5K_RX_FILTER_XRPOLL 0x00000040 /* Don't filter XR poll frame [5212+] */
+#define AR5K_RX_FILTER_PROBEREQ 0x00000080 /* Don't filter probe requests [5212+] */
+#define AR5K_RX_FILTER_PHYERR_5212 0x00000100 /* Don't filter phy errors [5212+] */
+#define AR5K_RX_FILTER_RADARERR_5212 0x00000200 /* Don't filter phy radar errors [5212+] */
+#define AR5K_RX_FILTER_PHYERR_5211 0x00000040 /* [5211] */
+#define AR5K_RX_FILTER_RADARERR_5211 0x00000080 /* [5211] */
+#define AR5K_RX_FILTER_PHYERR \
+ ((ah->ah_version == AR5K_AR5211 ? \
+ AR5K_RX_FILTER_PHYERR_5211 : AR5K_RX_FILTER_PHYERR_5212))
+#define AR5K_RX_FILTER_RADARERR \
+ ((ah->ah_version == AR5K_AR5211 ? \
+ AR5K_RX_FILTER_RADARERR_5211 : AR5K_RX_FILTER_RADARERR_5212))
+
+/*
+ * Multicast filter register (lower 32 bits)
+ */
+#define AR5K_MCAST_FILTER0_5210 0x8050
+#define AR5K_MCAST_FILTER0_5211 0x8040
+#define AR5K_MCAST_FILTER0 (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_MCAST_FILTER0_5210 : AR5K_MCAST_FILTER0_5211)
+
+/*
+ * Multicast filter register (higher 16 bits)
+ */
+#define AR5K_MCAST_FILTER1_5210 0x8054
+#define AR5K_MCAST_FILTER1_5211 0x8044
+#define AR5K_MCAST_FILTER1 (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_MCAST_FILTER1_5210 : AR5K_MCAST_FILTER1_5211)
+
+
+/*
+ * Transmit mask register (lower 32 bits) [5210]
+ */
+#define AR5K_TX_MASK0 0x8058
+
+/*
+ * Transmit mask register (higher 16 bits) [5210]
+ */
+#define AR5K_TX_MASK1 0x805c
+
+/*
+ * Clear transmit mask [5210]
+ */
+#define AR5K_CLR_TMASK 0x8060
+
+/*
+ * Trigger level register (before transmission) [5210]
+ */
+#define AR5K_TRIG_LVL 0x8064
+
+
+/*
+ * PCU Diagnostic register
+ *
+ * Used for tweaking/diagnostics.
+ */
+#define AR5K_DIAG_SW_5210 0x8068 /* Register Address [5210] */
+#define AR5K_DIAG_SW_5211 0x8048 /* Register Address [5211+] */
+#define AR5K_DIAG_SW (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_DIAG_SW_5210 : AR5K_DIAG_SW_5211)
+#define AR5K_DIAG_SW_DIS_WEP_ACK 0x00000001 /* Disable ACKs if WEP key is invalid */
+#define AR5K_DIAG_SW_DIS_ACK 0x00000002 /* Disable ACKs */
+#define AR5K_DIAG_SW_DIS_CTS 0x00000004 /* Disable CTSs */
+#define AR5K_DIAG_SW_DIS_ENC 0x00000008 /* Disable HW encryption */
+#define AR5K_DIAG_SW_DIS_DEC 0x00000010 /* Disable HW decryption */
+#define AR5K_DIAG_SW_DIS_TX_5210 0x00000020 /* Disable transmit [5210] */
+#define AR5K_DIAG_SW_DIS_RX_5210 0x00000040 /* Disable receive */
+#define AR5K_DIAG_SW_DIS_RX_5211 0x00000020
+#define AR5K_DIAG_SW_DIS_RX (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_DIAG_SW_DIS_RX_5210 : AR5K_DIAG_SW_DIS_RX_5211)
+#define AR5K_DIAG_SW_LOOP_BACK_5210 0x00000080 /* TX Data Loopback (i guess it goes with DIS_TX) [5210] */
+#define AR5K_DIAG_SW_LOOP_BACK_5211 0x00000040
+#define AR5K_DIAG_SW_LOOP_BACK (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_DIAG_SW_LOOP_BACK_5210 : AR5K_DIAG_SW_LOOP_BACK_5211)
+#define AR5K_DIAG_SW_CORR_FCS_5210 0x00000100 /* Generate invalid TX FCS */
+#define AR5K_DIAG_SW_CORR_FCS_5211 0x00000080
+#define AR5K_DIAG_SW_CORR_FCS (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_DIAG_SW_CORR_FCS_5210 : AR5K_DIAG_SW_CORR_FCS_5211)
+#define AR5K_DIAG_SW_CHAN_INFO_5210 0x00000200 /* Add 56 bytes of channel info before the frame data in the RX buffer */
+#define AR5K_DIAG_SW_CHAN_INFO_5211 0x00000100
+#define AR5K_DIAG_SW_CHAN_INFO (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_DIAG_SW_CHAN_INFO_5210 : AR5K_DIAG_SW_CHAN_INFO_5211)
+#define AR5K_DIAG_SW_EN_SCRAM_SEED_5210 0x00000400 /* Enable fixed scrambler seed */
+#define AR5K_DIAG_SW_EN_SCRAM_SEED_5211 0x00000200
+#define AR5K_DIAG_SW_EN_SCRAM_SEED (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_DIAG_SW_EN_SCRAM_SEED_5210 : AR5K_DIAG_SW_EN_SCRAM_SEED_5211)
+#define AR5K_DIAG_SW_ECO_ENABLE 0x00000400 /* [5211+] */
+#define AR5K_DIAG_SW_SCVRAM_SEED 0x0003f800 /* [5210] */
+#define AR5K_DIAG_SW_SCRAM_SEED_M 0x0001fc00 /* Scrambler seed mask */
+#define AR5K_DIAG_SW_SCRAM_SEED_S 10
+#define AR5K_DIAG_SW_DIS_SEQ_INC_5210 0x00040000 /* Disable seqnum increment (?)[5210] */
+#define AR5K_DIAG_SW_FRAME_NV0_5210 0x00080000
+#define AR5K_DIAG_SW_FRAME_NV0_5211 0x00020000 /* Accept frames of non-zero protocol number */
+#define AR5K_DIAG_SW_FRAME_NV0 (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_DIAG_SW_FRAME_NV0_5210 : AR5K_DIAG_SW_FRAME_NV0_5211)
+#define AR5K_DIAG_SW_OBSPT_M 0x000c0000 /* Observation point select (?) */
+#define AR5K_DIAG_SW_OBSPT_S 18
+#define AR5K_DIAG_SW_RX_CLEAR_HIGH 0x00100000 /* Ignore carrier sense */
+#define AR5K_DIAG_SW_IGNORE_CARR_SENSE 0x00200000 /* Ignore virtual carrier sense */
+#define AR5K_DIAG_SW_CHANNEL_IDLE_HIGH 0x00400000 /* Force channel idle high */
+#define AR5K_DIAG_SW_PHEAR_ME 0x00800000 /* ??? */
+
+/*
+ * TSF (clock) register (lower 32 bits)
+ */
+#define AR5K_TSF_L32_5210 0x806c
+#define AR5K_TSF_L32_5211 0x804c
+#define AR5K_TSF_L32 (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_TSF_L32_5210 : AR5K_TSF_L32_5211)
+
+/*
+ * TSF (clock) register (higher 32 bits)
+ */
+#define AR5K_TSF_U32_5210 0x8070
+#define AR5K_TSF_U32_5211 0x8050
+#define AR5K_TSF_U32 (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_TSF_U32_5210 : AR5K_TSF_U32_5211)
+
+/*
+ * Last beacon timestamp register (Read Only)
+ */
+#define AR5K_LAST_TSTP 0x8080
+
+/*
+ * ADDAC test register [5211+]
+ */
+#define AR5K_ADDAC_TEST 0x8054 /* Register Address */
+#define AR5K_ADDAC_TEST_TXCONT 0x00000001 /* Test continuous tx */
+#define AR5K_ADDAC_TEST_TST_MODE 0x00000002 /* Test mode */
+#define AR5K_ADDAC_TEST_LOOP_EN 0x00000004 /* Enable loop */
+#define AR5K_ADDAC_TEST_LOOP_LEN 0x00000008 /* Loop length (field) */
+#define AR5K_ADDAC_TEST_USE_U8 0x00004000 /* Use upper 8 bits */
+#define AR5K_ADDAC_TEST_MSB 0x00008000 /* State of MSB */
+#define AR5K_ADDAC_TEST_TRIG_SEL 0x00010000 /* Trigger select */
+#define AR5K_ADDAC_TEST_TRIG_PTY 0x00020000 /* Trigger polarity */
+#define AR5K_ADDAC_TEST_RXCONT 0x00040000 /* Continuous capture */
+#define AR5K_ADDAC_TEST_CAPTURE 0x00080000 /* Begin capture */
+#define AR5K_ADDAC_TEST_TST_ARM 0x00100000 /* ARM rx buffer for capture */
+
+/*
+ * Default antenna register [5211+]
+ */
+#define AR5K_DEFAULT_ANTENNA 0x8058
+
+/*
+ * Frame control QoS mask register (?) [5211+]
+ * (FC_QOS_MASK)
+ */
+#define AR5K_FRAME_CTL_QOSM 0x805c
+
+/*
+ * Seq mask register (?) [5211+]
+ */
+#define AR5K_SEQ_MASK 0x8060
+
+/*
+ * Retry count register [5210]
+ */
+#define AR5K_RETRY_CNT 0x8084 /* Register Address [5210] */
+#define AR5K_RETRY_CNT_SSH 0x0000003f /* Station short retry count (?) */
+#define AR5K_RETRY_CNT_SLG 0x00000fc0 /* Station long retry count (?) */
+
+/*
+ * Back-off status register [5210]
+ */
+#define AR5K_BACKOFF 0x8088 /* Register Address [5210] */
+#define AR5K_BACKOFF_CW 0x000003ff /* Backoff Contention Window (?) */
+#define AR5K_BACKOFF_CNT 0x03ff0000 /* Backoff count (?) */
+
+
+
+/*
+ * NAV register (current)
+ */
+#define AR5K_NAV_5210 0x808c
+#define AR5K_NAV_5211 0x8084
+#define AR5K_NAV (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_NAV_5210 : AR5K_NAV_5211)
+
+/*
+ * MIB counters:
+ *
+ * max value is 0xc000, if this is reached we get a MIB interrupt.
+ * they can be controlled via AR5K_MIBC and are cleared on read.
+ */
+
+/*
+ * RTS success (MIB counter)
+ */
+#define AR5K_RTS_OK_5210 0x8090
+#define AR5K_RTS_OK_5211 0x8088
+#define AR5K_RTS_OK (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_RTS_OK_5210 : AR5K_RTS_OK_5211)
+
+/*
+ * RTS failure (MIB counter)
+ */
+#define AR5K_RTS_FAIL_5210 0x8094
+#define AR5K_RTS_FAIL_5211 0x808c
+#define AR5K_RTS_FAIL (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_RTS_FAIL_5210 : AR5K_RTS_FAIL_5211)
+
+/*
+ * ACK failure (MIB counter)
+ */
+#define AR5K_ACK_FAIL_5210 0x8098
+#define AR5K_ACK_FAIL_5211 0x8090
+#define AR5K_ACK_FAIL (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_ACK_FAIL_5210 : AR5K_ACK_FAIL_5211)
+
+/*
+ * FCS failure (MIB counter)
+ */
+#define AR5K_FCS_FAIL_5210 0x809c
+#define AR5K_FCS_FAIL_5211 0x8094
+#define AR5K_FCS_FAIL (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_FCS_FAIL_5210 : AR5K_FCS_FAIL_5211)
+
+/*
+ * Beacon count register
+ */
+#define AR5K_BEACON_CNT_5210 0x80a0
+#define AR5K_BEACON_CNT_5211 0x8098
+#define AR5K_BEACON_CNT (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_BEACON_CNT_5210 : AR5K_BEACON_CNT_5211)
+
+
+/*===5212 Specific PCU registers===*/
+
+/*
+ * Transmit power control register
+ */
+#define AR5K_TPC 0x80e8
+#define AR5K_TPC_ACK 0x0000003f /* ack frames */
+#define AR5K_TPC_ACK_S 0
+#define AR5K_TPC_CTS 0x00003f00 /* cts frames */
+#define AR5K_TPC_CTS_S 8
+#define AR5K_TPC_CHIRP 0x003f0000 /* chirp frames */
+#define AR5K_TPC_CHIRP_S 16
+#define AR5K_TPC_DOPPLER 0x0f000000 /* doppler chirp span */
+#define AR5K_TPC_DOPPLER_S 24
+
+/*
+ * XR (eXtended Range) mode register
+ */
+#define AR5K_XRMODE 0x80c0 /* Register Address */
+#define AR5K_XRMODE_POLL_TYPE_M 0x0000003f /* Mask for Poll type (?) */
+#define AR5K_XRMODE_POLL_TYPE_S 0
+#define AR5K_XRMODE_POLL_SUBTYPE_M 0x0000003c /* Mask for Poll subtype (?) */
+#define AR5K_XRMODE_POLL_SUBTYPE_S 2
+#define AR5K_XRMODE_POLL_WAIT_ALL 0x00000080 /* Wait for poll */
+#define AR5K_XRMODE_SIFS_DELAY 0x000fff00 /* Mask for SIFS delay */
+#define AR5K_XRMODE_FRAME_HOLD_M 0xfff00000 /* Mask for frame hold (?) */
+#define AR5K_XRMODE_FRAME_HOLD_S 20
+
+/*
+ * XR delay register
+ */
+#define AR5K_XRDELAY 0x80c4 /* Register Address */
+#define AR5K_XRDELAY_SLOT_DELAY_M 0x0000ffff /* Mask for slot delay */
+#define AR5K_XRDELAY_SLOT_DELAY_S 0
+#define AR5K_XRDELAY_CHIRP_DELAY_M 0xffff0000 /* Mask for CHIRP data delay */
+#define AR5K_XRDELAY_CHIRP_DELAY_S 16
+
+/*
+ * XR timeout register
+ */
+#define AR5K_XRTIMEOUT 0x80c8 /* Register Address */
+#define AR5K_XRTIMEOUT_CHIRP_M 0x0000ffff /* Mask for CHIRP timeout */
+#define AR5K_XRTIMEOUT_CHIRP_S 0
+#define AR5K_XRTIMEOUT_POLL_M 0xffff0000 /* Mask for Poll timeout */
+#define AR5K_XRTIMEOUT_POLL_S 16
+
+/*
+ * XR chirp register
+ */
+#define AR5K_XRCHIRP 0x80cc /* Register Address */
+#define AR5K_XRCHIRP_SEND 0x00000001 /* Send CHIRP */
+#define AR5K_XRCHIRP_GAP 0xffff0000 /* Mask for CHIRP gap (?) */
+
+/*
+ * XR stomp register
+ */
+#define AR5K_XRSTOMP 0x80d0 /* Register Address */
+#define AR5K_XRSTOMP_TX 0x00000001 /* Stomp Tx (?) */
+#define AR5K_XRSTOMP_RX 0x00000002 /* Stomp Rx (?) */
+#define AR5K_XRSTOMP_TX_RSSI 0x00000004 /* Stomp Tx RSSI (?) */
+#define AR5K_XRSTOMP_TX_BSSID 0x00000008 /* Stomp Tx BSSID (?) */
+#define AR5K_XRSTOMP_DATA 0x00000010 /* Stomp data (?)*/
+#define AR5K_XRSTOMP_RSSI_THRES 0x0000ff00 /* Mask for XR RSSI threshold */
+
+/*
+ * First enhanced sleep register
+ */
+#define AR5K_SLEEP0 0x80d4 /* Register Address */
+#define AR5K_SLEEP0_NEXT_DTIM 0x0007ffff /* Mask for next DTIM (?) */
+#define AR5K_SLEEP0_NEXT_DTIM_S 0
+#define AR5K_SLEEP0_ASSUME_DTIM 0x00080000 /* Assume DTIM */
+#define AR5K_SLEEP0_ENH_SLEEP_EN 0x00100000 /* Enable enhanced sleep control */
+#define AR5K_SLEEP0_CABTO 0xff000000 /* Mask for CAB Time Out */
+#define AR5K_SLEEP0_CABTO_S 24
+
+/*
+ * Second enhanced sleep register
+ */
+#define AR5K_SLEEP1 0x80d8 /* Register Address */
+#define AR5K_SLEEP1_NEXT_TIM 0x0007ffff /* Mask for next TIM (?) */
+#define AR5K_SLEEP1_NEXT_TIM_S 0
+#define AR5K_SLEEP1_BEACON_TO 0xff000000 /* Mask for Beacon Time Out */
+#define AR5K_SLEEP1_BEACON_TO_S 24
+
+/*
+ * Third enhanced sleep register
+ */
+#define AR5K_SLEEP2 0x80dc /* Register Address */
+#define AR5K_SLEEP2_TIM_PER 0x0000ffff /* Mask for TIM period (?) */
+#define AR5K_SLEEP2_TIM_PER_S 0
+#define AR5K_SLEEP2_DTIM_PER 0xffff0000 /* Mask for DTIM period (?) */
+#define AR5K_SLEEP2_DTIM_PER_S 16
+
+/*
+ * TX power control (TPC) register
+ *
+ * XXX: PCDAC steps (0.5dBm) or dBm ?
+ *
+ */
+#define AR5K_TXPC 0x80e8 /* Register Address */
+#define AR5K_TXPC_ACK_M 0x0000003f /* ACK tx power */
+#define AR5K_TXPC_ACK_S 0
+#define AR5K_TXPC_CTS_M 0x00003f00 /* CTS tx power */
+#define AR5K_TXPC_CTS_S 8
+#define AR5K_TXPC_CHIRP_M 0x003f0000 /* CHIRP tx power */
+#define AR5K_TXPC_CHIRP_S 16
+#define AR5K_TXPC_DOPPLER 0x0f000000 /* Doppler chirp span (?) */
+#define AR5K_TXPC_DOPPLER_S 24
+
+/*
+ * Profile count registers
+ *
+ * These registers can be cleared and frozen with ATH5K_MIBC, but they do not
+ * generate a MIB interrupt.
+ * Instead of overflowing, they shift by one bit to the right. All registers
+ * shift together, i.e. when one reaches the max, all shift at the same time by
+ * one bit to the right. This way we should always get consistent values.
+ */
+#define AR5K_PROFCNT_TX 0x80ec /* Tx count */
+#define AR5K_PROFCNT_RX 0x80f0 /* Rx count */
+#define AR5K_PROFCNT_RXCLR 0x80f4 /* Busy count */
+#define AR5K_PROFCNT_CYCLE 0x80f8 /* Cycle counter */
+
+/*
+ * Quiet period control registers
+ */
+#define AR5K_QUIET_CTL1 0x80fc /* Register Address */
+#define AR5K_QUIET_CTL1_NEXT_QT_TSF 0x0000ffff /* Next quiet period TSF (TU) */
+#define AR5K_QUIET_CTL1_NEXT_QT_TSF_S 0
+#define AR5K_QUIET_CTL1_QT_EN 0x00010000 /* Enable quiet period */
+#define AR5K_QUIET_CTL1_ACK_CTS_EN 0x00020000 /* Send ACK/CTS during quiet period */
+
+#define AR5K_QUIET_CTL2 0x8100 /* Register Address */
+#define AR5K_QUIET_CTL2_QT_PER 0x0000ffff /* Mask for quiet period periodicity */
+#define AR5K_QUIET_CTL2_QT_PER_S 0
+#define AR5K_QUIET_CTL2_QT_DUR 0xffff0000 /* Mask for quiet period duration */
+#define AR5K_QUIET_CTL2_QT_DUR_S 16
+
+/*
+ * TSF parameter register
+ */
+#define AR5K_TSF_PARM 0x8104 /* Register Address */
+#define AR5K_TSF_PARM_INC 0x000000ff /* Mask for TSF increment */
+#define AR5K_TSF_PARM_INC_S 0
+
+/*
+ * QoS NOACK policy
+ */
+#define AR5K_QOS_NOACK 0x8108 /* Register Address */
+#define AR5K_QOS_NOACK_2BIT_VALUES 0x0000000f /* ??? */
+#define AR5K_QOS_NOACK_2BIT_VALUES_S 0
+#define AR5K_QOS_NOACK_BIT_OFFSET 0x00000070 /* ??? */
+#define AR5K_QOS_NOACK_BIT_OFFSET_S 4
+#define AR5K_QOS_NOACK_BYTE_OFFSET 0x00000180 /* ??? */
+#define AR5K_QOS_NOACK_BYTE_OFFSET_S 7
+
+/*
+ * PHY error filter register
+ */
+#define AR5K_PHY_ERR_FIL 0x810c
+#define AR5K_PHY_ERR_FIL_RADAR 0x00000020 /* Radar signal */
+#define AR5K_PHY_ERR_FIL_OFDM 0x00020000 /* OFDM false detect (ANI) */
+#define AR5K_PHY_ERR_FIL_CCK 0x02000000 /* CCK false detect (ANI) */
+
+/*
+ * XR latency register
+ */
+#define AR5K_XRLAT_TX 0x8110
+
+/*
+ * ACK SIFS register
+ */
+#define AR5K_ACKSIFS 0x8114 /* Register Address */
+#define AR5K_ACKSIFS_INC 0x00000000 /* ACK SIFS Increment (field) */
+
+/*
+ * MIC QoS control register (?)
+ */
+#define AR5K_MIC_QOS_CTL 0x8118 /* Register Address */
+#define AR5K_MIC_QOS_CTL_OFF(_n) (1 << (_n * 2))
+#define AR5K_MIC_QOS_CTL_MQ_EN 0x00010000 /* Enable MIC QoS */
+
+/*
+ * MIC QoS select register (?)
+ */
+#define AR5K_MIC_QOS_SEL 0x811c
+#define AR5K_MIC_QOS_SEL_OFF(_n) (1 << (_n * 4))
+
+/*
+ * Misc mode control register (?)
+ */
+#define AR5K_MISC_MODE 0x8120 /* Register Address */
+#define AR5K_MISC_MODE_FBSSID_MATCH 0x00000001 /* Force BSSID match */
+#define AR5K_MISC_MODE_ACKSIFS_MEM 0x00000002 /* ACK SIFS memory (?) */
+#define AR5K_MISC_MODE_COMBINED_MIC 0x00000004 /* use rx/tx MIC key */
+/* more bits */
+
+/*
+ * OFDM Filter counter
+ */
+#define AR5K_OFDM_FIL_CNT 0x8124
+
+/*
+ * CCK Filter counter
+ */
+#define AR5K_CCK_FIL_CNT 0x8128
+
+/*
+ * PHY Error Counters (same masks as AR5K_PHY_ERR_FIL)
+ */
+#define AR5K_PHYERR_CNT1 0x812c
+#define AR5K_PHYERR_CNT1_MASK 0x8130
+
+#define AR5K_PHYERR_CNT2 0x8134
+#define AR5K_PHYERR_CNT2_MASK 0x8138
+
+/* if the PHY Error Counters reach this maximum, we get MIB interrupts */
+#define ATH5K_PHYERR_CNT_MAX 0x00c00000
+
+/*
+ * TSF Threshold register (?)
+ */
+#define AR5K_TSF_THRES 0x813c
+
+/*
+ * TODO: Wake On Wireless registers
+ * Range: 0x8147 - 0x818c
+ */
+
+/*
+ * Rate -> ACK SIFS mapping table (32 entries)
+ */
+#define AR5K_RATE_ACKSIFS_BASE 0x8680 /* Register Address */
+#define AR5K_RATE_ACKSIFS(_n) (AR5K_RATE_ACKSIFS_BSE + ((_n) << 2))
+#define AR5K_RATE_ACKSIFS_NORMAL 0x00000001 /* Normal SIFS (field) */
+#define AR5K_RATE_ACKSIFS_TURBO 0x00000400 /* Turbo SIFS (field) */
+
+/*
+ * Rate -> duration mapping table (32 entries)
+ */
+#define AR5K_RATE_DUR_BASE 0x8700
+#define AR5K_RATE_DUR(_n) (AR5K_RATE_DUR_BASE + ((_n) << 2))
+
+/*
+ * Rate -> db mapping table
+ * (8 entries, each one has 4 8bit fields)
+ */
+#define AR5K_RATE2DB_BASE 0x87c0
+#define AR5K_RATE2DB(_n) (AR5K_RATE2DB_BASE + ((_n) << 2))
+
+/*
+ * db -> Rate mapping table
+ * (8 entries, each one has 4 8bit fields)
+ */
+#define AR5K_DB2RATE_BASE 0x87e0
+#define AR5K_DB2RATE(_n) (AR5K_DB2RATE_BASE + ((_n) << 2))
+
+/*===5212 end===*/
+
+#define AR5K_KEYTABLE_SIZE_5210 64
+#define AR5K_KEYTABLE_SIZE_5211 128
+
+/*===PHY REGISTERS===*/
+
+/*
+ * PHY registers start
+ */
+#define AR5K_PHY_BASE 0x9800
+#define AR5K_PHY(_n) (AR5K_PHY_BASE + ((_n) << 2))
+
+/*
+ * TST_2 (Misc config parameters)
+ */
+#define AR5K_PHY_TST2 0x9800 /* Register Address */
+#define AR5K_PHY_TST2_TRIG_SEL 0x00000007 /* Trigger select (?)*/
+#define AR5K_PHY_TST2_TRIG 0x00000010 /* Trigger (?) */
+#define AR5K_PHY_TST2_CBUS_MODE 0x00000060 /* Cardbus mode (?) */
+#define AR5K_PHY_TST2_CLK32 0x00000400 /* CLK_OUT is CLK32 (32kHz external) */
+#define AR5K_PHY_TST2_CHANCOR_DUMP_EN 0x00000800 /* Enable Chancor dump (?) */
+#define AR5K_PHY_TST2_EVEN_CHANCOR_DUMP 0x00001000 /* Even Chancor dump (?) */
+#define AR5K_PHY_TST2_RFSILENT_EN 0x00002000 /* Enable RFSILENT */
+#define AR5K_PHY_TST2_ALT_RFDATA 0x00004000 /* Alternate RFDATA (5-2GHz switch ?) */
+#define AR5K_PHY_TST2_MINI_OBS_EN 0x00008000 /* Enable mini OBS (?) */
+#define AR5K_PHY_TST2_RX2_IS_RX5_INV 0x00010000 /* 2GHz rx path is the 5GHz path inverted (?) */
+#define AR5K_PHY_TST2_SLOW_CLK160 0x00020000 /* Slow CLK160 (?) */
+#define AR5K_PHY_TST2_AGC_OBS_SEL_3 0x00040000 /* AGC OBS Select 3 (?) */
+#define AR5K_PHY_TST2_BBB_OBS_SEL 0x00080000 /* BB OBS Select (field ?) */
+#define AR5K_PHY_TST2_ADC_OBS_SEL 0x00800000 /* ADC OBS Select (field ?) */
+#define AR5K_PHY_TST2_RX_CLR_SEL 0x08000000 /* RX Clear Select (?) */
+#define AR5K_PHY_TST2_FORCE_AGC_CLR 0x10000000 /* Force AGC clear (?) */
+#define AR5K_PHY_SHIFT_2GHZ 0x00004007 /* Used to access 2GHz radios */
+#define AR5K_PHY_SHIFT_5GHZ 0x00000007 /* Used to access 5GHz radios (default) */
+
+/*
+ * PHY frame control register [5110] /turbo mode register [5111+]
+ *
+ * There is another frame control register for [5111+]
+ * at address 0x9944 (see below) but the 2 first flags
+ * are common here between 5110 frame control register
+ * and [5111+] turbo mode register, so this also works as
+ * a "turbo mode register" for 5110. We treat this one as
+ * a frame control register for 5110 below.
+ */
+#define AR5K_PHY_TURBO 0x9804 /* Register Address */
+#define AR5K_PHY_TURBO_MODE 0x00000001 /* Enable turbo mode */
+#define AR5K_PHY_TURBO_SHORT 0x00000002 /* Set short symbols to turbo mode */
+#define AR5K_PHY_TURBO_MIMO 0x00000004 /* Set turbo for mimo */
+
+/*
+ * PHY agility command register
+ * (aka TST_1)
+ */
+#define AR5K_PHY_AGC 0x9808 /* Register Address */
+#define AR5K_PHY_TST1 0x9808
+#define AR5K_PHY_AGC_DISABLE 0x08000000 /* Disable AGC to A2 (?)*/
+#define AR5K_PHY_TST1_TXHOLD 0x00003800 /* Set tx hold (?) */
+#define AR5K_PHY_TST1_TXSRC_SRC 0x00000002 /* Used with bit 7 (?) */
+#define AR5K_PHY_TST1_TXSRC_SRC_S 1
+#define AR5K_PHY_TST1_TXSRC_ALT 0x00000080 /* Set input to tsdac (?) */
+#define AR5K_PHY_TST1_TXSRC_ALT_S 7
+
+
+/*
+ * PHY timing register 3 [5112+]
+ */
+#define AR5K_PHY_TIMING_3 0x9814
+#define AR5K_PHY_TIMING_3_DSC_MAN 0xfffe0000
+#define AR5K_PHY_TIMING_3_DSC_MAN_S 17
+#define AR5K_PHY_TIMING_3_DSC_EXP 0x0001e000
+#define AR5K_PHY_TIMING_3_DSC_EXP_S 13
+
+/*
+ * PHY chip revision register
+ */
+#define AR5K_PHY_CHIP_ID 0x9818
+
+/*
+ * PHY activation register
+ */
+#define AR5K_PHY_ACT 0x981c /* Register Address */
+#define AR5K_PHY_ACT_ENABLE 0x00000001 /* Activate PHY */
+#define AR5K_PHY_ACT_DISABLE 0x00000002 /* Deactivate PHY */
+
+/*
+ * PHY RF control registers
+ */
+#define AR5K_PHY_RF_CTL2 0x9824 /* Register Address */
+#define AR5K_PHY_RF_CTL2_TXF2TXD_START 0x0000000f /* TX frame to TX data start */
+#define AR5K_PHY_RF_CTL2_TXF2TXD_START_S 0
+
+#define AR5K_PHY_RF_CTL3 0x9828 /* Register Address */
+#define AR5K_PHY_RF_CTL3_TXE2XLNA_ON 0x0000ff00 /* TX end to XLNA on */
+#define AR5K_PHY_RF_CTL3_TXE2XLNA_ON_S 8
+
+#define AR5K_PHY_ADC_CTL 0x982c
+#define AR5K_PHY_ADC_CTL_INBUFGAIN_OFF 0x00000003
+#define AR5K_PHY_ADC_CTL_INBUFGAIN_OFF_S 0
+#define AR5K_PHY_ADC_CTL_PWD_DAC_OFF 0x00002000
+#define AR5K_PHY_ADC_CTL_PWD_BAND_GAP_OFF 0x00004000
+#define AR5K_PHY_ADC_CTL_PWD_ADC_OFF 0x00008000
+#define AR5K_PHY_ADC_CTL_INBUFGAIN_ON 0x00030000
+#define AR5K_PHY_ADC_CTL_INBUFGAIN_ON_S 16
+
+#define AR5K_PHY_RF_CTL4 0x9834 /* Register Address */
+#define AR5K_PHY_RF_CTL4_TXF2XPA_A_ON 0x00000001 /* TX frame to XPA A on (field) */
+#define AR5K_PHY_RF_CTL4_TXF2XPA_B_ON 0x00000100 /* TX frame to XPA B on (field) */
+#define AR5K_PHY_RF_CTL4_TXE2XPA_A_OFF 0x00010000 /* TX end to XPA A off (field) */
+#define AR5K_PHY_RF_CTL4_TXE2XPA_B_OFF 0x01000000 /* TX end to XPA B off (field) */
+
+/*
+ * Pre-Amplifier control register
+ * (XPA -> external pre-amplifier)
+ */
+#define AR5K_PHY_PA_CTL 0x9838 /* Register Address */
+#define AR5K_PHY_PA_CTL_XPA_A_HI 0x00000001 /* XPA A high (?) */
+#define AR5K_PHY_PA_CTL_XPA_B_HI 0x00000002 /* XPA B high (?) */
+#define AR5K_PHY_PA_CTL_XPA_A_EN 0x00000004 /* Enable XPA A */
+#define AR5K_PHY_PA_CTL_XPA_B_EN 0x00000008 /* Enable XPA B */
+
+/*
+ * PHY settling register
+ */
+#define AR5K_PHY_SETTLING 0x9844 /* Register Address */
+#define AR5K_PHY_SETTLING_AGC 0x0000007f /* AGC settling time */
+#define AR5K_PHY_SETTLING_AGC_S 0
+#define AR5K_PHY_SETTLING_SWITCH 0x00003f80 /* Switch settling time */
+#define AR5K_PHY_SETTLING_SWITCH_S 7
+
+/*
+ * PHY Gain registers
+ */
+#define AR5K_PHY_GAIN 0x9848 /* Register Address */
+#define AR5K_PHY_GAIN_TXRX_ATTEN 0x0003f000 /* TX-RX Attenuation */
+#define AR5K_PHY_GAIN_TXRX_ATTEN_S 12
+#define AR5K_PHY_GAIN_TXRX_RF_MAX 0x007c0000
+#define AR5K_PHY_GAIN_TXRX_RF_MAX_S 18
+
+#define AR5K_PHY_GAIN_OFFSET 0x984c /* Register Address */
+#define AR5K_PHY_GAIN_OFFSET_RXTX_FLAG 0x00020000 /* RX-TX flag (?) */
+
+/*
+ * Desired ADC/PGA size register
+ * (for more infos read ANI patent)
+ */
+#define AR5K_PHY_DESIRED_SIZE 0x9850 /* Register Address */
+#define AR5K_PHY_DESIRED_SIZE_ADC 0x000000ff /* ADC desired size */
+#define AR5K_PHY_DESIRED_SIZE_ADC_S 0
+#define AR5K_PHY_DESIRED_SIZE_PGA 0x0000ff00 /* PGA desired size */
+#define AR5K_PHY_DESIRED_SIZE_PGA_S 8
+#define AR5K_PHY_DESIRED_SIZE_TOT 0x0ff00000 /* Total desired size */
+#define AR5K_PHY_DESIRED_SIZE_TOT_S 20
+
+/*
+ * PHY signal register
+ * (for more infos read ANI patent)
+ */
+#define AR5K_PHY_SIG 0x9858 /* Register Address */
+#define AR5K_PHY_SIG_FIRSTEP 0x0003f000 /* FIRSTEP */
+#define AR5K_PHY_SIG_FIRSTEP_S 12
+#define AR5K_PHY_SIG_FIRPWR 0x03fc0000 /* FIPWR */
+#define AR5K_PHY_SIG_FIRPWR_S 18
+
+/*
+ * PHY coarse agility control register
+ * (for more infos read ANI patent)
+ */
+#define AR5K_PHY_AGCCOARSE 0x985c /* Register Address */
+#define AR5K_PHY_AGCCOARSE_LO 0x00007f80 /* AGC Coarse low */
+#define AR5K_PHY_AGCCOARSE_LO_S 7
+#define AR5K_PHY_AGCCOARSE_HI 0x003f8000 /* AGC Coarse high */
+#define AR5K_PHY_AGCCOARSE_HI_S 15
+
+/*
+ * PHY agility control register
+ */
+#define AR5K_PHY_AGCCTL 0x9860 /* Register address */
+#define AR5K_PHY_AGCCTL_CAL 0x00000001 /* Enable PHY calibration */
+#define AR5K_PHY_AGCCTL_NF 0x00000002 /* Enable Noise Floor calibration */
+#define AR5K_PHY_AGCCTL_OFDM_DIV_DIS 0x00000008 /* Disable antenna diversity on OFDM modes */
+#define AR5K_PHY_AGCCTL_NF_EN 0x00008000 /* Enable nf calibration to happen (?) */
+#define AR5K_PHY_AGCTL_FLTR_CAL 0x00010000 /* Allow filter calibration (?) */
+#define AR5K_PHY_AGCCTL_NF_NOUPDATE 0x00020000 /* Don't update nf automatically */
+
+/*
+ * PHY noise floor status register (CCA = Clear Channel Assessment)
+ */
+#define AR5K_PHY_NF 0x9864 /* Register address */
+#define AR5K_PHY_NF_M 0x000001ff /* Noise floor, written to hardware in 1/2 dBm units */
+#define AR5K_PHY_NF_SVAL(_n) (((_n) & AR5K_PHY_NF_M) | (1 << 9))
+#define AR5K_PHY_NF_THRESH62 0x0007f000 /* Thresh62 -check ANI patent- (field) */
+#define AR5K_PHY_NF_THRESH62_S 12
+#define AR5K_PHY_NF_MINCCA_PWR 0x0ff80000 /* Minimum measured noise level, read from hardware in 1 dBm units */
+#define AR5K_PHY_NF_MINCCA_PWR_S 19
+
+/*
+ * PHY ADC saturation register [5110]
+ */
+#define AR5K_PHY_ADCSAT 0x9868
+#define AR5K_PHY_ADCSAT_ICNT 0x0001f800
+#define AR5K_PHY_ADCSAT_ICNT_S 11
+#define AR5K_PHY_ADCSAT_THR 0x000007e0
+#define AR5K_PHY_ADCSAT_THR_S 5
+
+/*
+ * PHY Weak ofdm signal detection threshold registers (ANI) [5212+]
+ */
+
+/* High thresholds */
+#define AR5K_PHY_WEAK_OFDM_HIGH_THR 0x9868
+#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT 0x0000001f
+#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT_S 0
+#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M1 0x00fe0000
+#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M1_S 17
+#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2 0x7f000000
+#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_S 24
+
+/* Low thresholds */
+#define AR5K_PHY_WEAK_OFDM_LOW_THR 0x986c
+#define AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN 0x00000001
+#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT 0x00003f00
+#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT_S 8
+#define AR5K_PHY_WEAK_OFDM_LOW_THR_M1 0x001fc000
+#define AR5K_PHY_WEAK_OFDM_LOW_THR_M1_S 14
+#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2 0x0fe00000
+#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_S 21
+
+
+/*
+ * PHY sleep registers [5112+]
+ */
+#define AR5K_PHY_SCR 0x9870
+
+#define AR5K_PHY_SLMT 0x9874
+#define AR5K_PHY_SLMT_32MHZ 0x0000007f
+
+#define AR5K_PHY_SCAL 0x9878
+#define AR5K_PHY_SCAL_32MHZ 0x0000000e
+#define AR5K_PHY_SCAL_32MHZ_5311 0x00000008
+#define AR5K_PHY_SCAL_32MHZ_2417 0x0000000a
+#define AR5K_PHY_SCAL_32MHZ_HB63 0x00000032
+
+/*
+ * PHY PLL (Phase Locked Loop) control register
+ */
+#define AR5K_PHY_PLL 0x987c
+#define AR5K_PHY_PLL_20MHZ 0x00000013 /* For half rate (?) */
+/* 40MHz -> 5GHz band */
+#define AR5K_PHY_PLL_40MHZ_5211 0x00000018
+#define AR5K_PHY_PLL_40MHZ_5212 0x000000aa
+#define AR5K_PHY_PLL_40MHZ_5413 0x00000004
+#define AR5K_PHY_PLL_40MHZ (ah->ah_version == AR5K_AR5211 ? \
+ AR5K_PHY_PLL_40MHZ_5211 : AR5K_PHY_PLL_40MHZ_5212)
+/* 44MHz -> 2.4GHz band */
+#define AR5K_PHY_PLL_44MHZ_5211 0x00000019
+#define AR5K_PHY_PLL_44MHZ_5212 0x000000ab
+#define AR5K_PHY_PLL_44MHZ (ah->ah_version == AR5K_AR5211 ? \
+ AR5K_PHY_PLL_44MHZ_5211 : AR5K_PHY_PLL_44MHZ_5212)
+
+#define AR5K_PHY_PLL_RF5111 0x00000000
+#define AR5K_PHY_PLL_RF5112 0x00000040
+#define AR5K_PHY_PLL_HALF_RATE 0x00000100
+#define AR5K_PHY_PLL_QUARTER_RATE 0x00000200
+
+/*
+ * RF Buffer register
+ *
+ * It's obvious from the code that 0x989c is the buffer register but
+ * for the other special registers that we write to after sending each
+ * packet, i have no idea. So I'll name them BUFFER_CONTROL_X registers
+ * for now. It's interesting that they are also used for some other operations.
+ */
+
+#define AR5K_RF_BUFFER 0x989c
+#define AR5K_RF_BUFFER_CONTROL_0 0x98c0 /* Channel on 5110 */
+#define AR5K_RF_BUFFER_CONTROL_1 0x98c4 /* Bank 7 on 5112 */
+#define AR5K_RF_BUFFER_CONTROL_2 0x98cc /* Bank 7 on 5111 */
+
+#define AR5K_RF_BUFFER_CONTROL_3 0x98d0 /* Bank 2 on 5112 */
+ /* Channel set on 5111 */
+ /* Used to read radio revision*/
+
+#define AR5K_RF_BUFFER_CONTROL_4 0x98d4 /* RF Stage register on 5110 */
+ /* Bank 0,1,2,6 on 5111 */
+ /* Bank 1 on 5112 */
+ /* Used during activation on 5111 */
+
+#define AR5K_RF_BUFFER_CONTROL_5 0x98d8 /* Bank 3 on 5111 */
+ /* Used during activation on 5111 */
+ /* Channel on 5112 */
+ /* Bank 6 on 5112 */
+
+#define AR5K_RF_BUFFER_CONTROL_6 0x98dc /* Bank 3 on 5112 */
+
+/*
+ * PHY RF stage register [5210]
+ */
+#define AR5K_PHY_RFSTG 0x98d4
+#define AR5K_PHY_RFSTG_DISABLE 0x00000021
+
+/*
+ * BIN masks (?)
+ */
+#define AR5K_PHY_BIN_MASK_1 0x9900
+#define AR5K_PHY_BIN_MASK_2 0x9904
+#define AR5K_PHY_BIN_MASK_3 0x9908
+
+#define AR5K_PHY_BIN_MASK_CTL 0x990c
+#define AR5K_PHY_BIN_MASK_CTL_MASK_4 0x00003fff
+#define AR5K_PHY_BIN_MASK_CTL_MASK_4_S 0
+#define AR5K_PHY_BIN_MASK_CTL_RATE 0xff000000
+#define AR5K_PHY_BIN_MASK_CTL_RATE_S 24
+
+/*
+ * PHY Antenna control register
+ */
+#define AR5K_PHY_ANT_CTL 0x9910 /* Register Address */
+#define AR5K_PHY_ANT_CTL_TXRX_EN 0x00000001 /* Enable TX/RX (?) */
+#define AR5K_PHY_ANT_CTL_SECTORED_ANT 0x00000004 /* Sectored Antenna */
+#define AR5K_PHY_ANT_CTL_HITUNE5 0x00000008 /* Hitune5 (?) */
+#define AR5K_PHY_ANT_CTL_SWTABLE_IDLE 0x000003f0 /* Switch table idle (?) */
+#define AR5K_PHY_ANT_CTL_SWTABLE_IDLE_S 4
+
+/*
+ * PHY receiver delay register [5111+]
+ */
+#define AR5K_PHY_RX_DELAY 0x9914 /* Register Address */
+#define AR5K_PHY_RX_DELAY_M 0x00003fff /* Mask for RX activate to receive delay (/100ns) */
+
+/*
+ * PHY max rx length register (?) [5111]
+ */
+#define AR5K_PHY_MAX_RX_LEN 0x991c
+
+/*
+ * PHY timing register 4
+ * I(nphase)/Q(adrature) calibration register [5111+]
+ */
+#define AR5K_PHY_IQ 0x9920 /* Register Address */
+#define AR5K_PHY_IQ_CORR_Q_Q_COFF 0x0000001f /* Mask for q correction info */
+#define AR5K_PHY_IQ_CORR_Q_Q_COFF_S 0
+#define AR5K_PHY_IQ_CORR_Q_I_COFF 0x000007e0 /* Mask for i correction info */
+#define AR5K_PHY_IQ_CORR_Q_I_COFF_S 5
+#define AR5K_PHY_IQ_CORR_ENABLE 0x00000800 /* Enable i/q correction */
+#define AR5K_PHY_IQ_CAL_NUM_LOG_MAX 0x0000f000 /* Mask for max number of samples in log scale */
+#define AR5K_PHY_IQ_CAL_NUM_LOG_MAX_S 12
+#define AR5K_PHY_IQ_RUN 0x00010000 /* Run i/q calibration */
+#define AR5K_PHY_IQ_USE_PT_DF 0x00020000 /* Use pilot track df (?) */
+#define AR5K_PHY_IQ_EARLY_TRIG_THR 0x00200000 /* Early trigger threshold (?) (field) */
+#define AR5K_PHY_IQ_PILOT_MASK_EN 0x10000000 /* Enable pilot mask (?) */
+#define AR5K_PHY_IQ_CHAN_MASK_EN 0x20000000 /* Enable channel mask (?) */
+#define AR5K_PHY_IQ_SPUR_FILT_EN 0x40000000 /* Enable spur filter */
+#define AR5K_PHY_IQ_SPUR_RSSI_EN 0x80000000 /* Enable spur rssi */
+
+/*
+ * PHY timing register 5
+ * OFDM Self-correlator Cyclic RSSI threshold params
+ * (Check out bb_cycpwr_thr1 on ANI patent)
+ */
+#define AR5K_PHY_OFDM_SELFCORR 0x9924 /* Register Address */
+#define AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1_EN 0x00000001 /* Enable cyclic RSSI thr 1 */
+#define AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1 0x000000fe /* Mask for Cyclic RSSI threshold 1 */
+#define AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1_S 1
+#define AR5K_PHY_OFDM_SELFCORR_CYPWR_THR3 0x00000100 /* Cyclic RSSI threshold 3 (field) (?) */
+#define AR5K_PHY_OFDM_SELFCORR_RSSI_1ATHR_EN 0x00008000 /* Enable 1A RSSI threshold (?) */
+#define AR5K_PHY_OFDM_SELFCORR_RSSI_1ATHR 0x00010000 /* 1A RSSI threshold (field) (?) */
+#define AR5K_PHY_OFDM_SELFCORR_LSCTHR_HIRSSI 0x00800000 /* Long sc threshold hi rssi (?) */
+
+/*
+ * PHY-only warm reset register
+ */
+#define AR5K_PHY_WARM_RESET 0x9928
+
+/*
+ * PHY-only control register
+ */
+#define AR5K_PHY_CTL 0x992c /* Register Address */
+#define AR5K_PHY_CTL_RX_DRAIN_RATE 0x00000001 /* RX drain rate (?) */
+#define AR5K_PHY_CTL_LATE_TX_SIG_SYM 0x00000002 /* Late tx signal symbol (?) */
+#define AR5K_PHY_CTL_GEN_SCRAMBLER 0x00000004 /* Generate scrambler */
+#define AR5K_PHY_CTL_TX_ANT_SEL 0x00000008 /* TX antenna select */
+#define AR5K_PHY_CTL_TX_ANT_STATIC 0x00000010 /* Static TX antenna */
+#define AR5K_PHY_CTL_RX_ANT_SEL 0x00000020 /* RX antenna select */
+#define AR5K_PHY_CTL_RX_ANT_STATIC 0x00000040 /* Static RX antenna */
+#define AR5K_PHY_CTL_LOW_FREQ_SLE_EN 0x00000080 /* Enable low freq sleep */
+
+/*
+ * PHY PAPD probe register [5111+]
+ */
+#define AR5K_PHY_PAPD_PROBE 0x9930
+#define AR5K_PHY_PAPD_PROBE_SH_HI_PAR 0x00000001
+#define AR5K_PHY_PAPD_PROBE_PCDAC_BIAS 0x00000002
+#define AR5K_PHY_PAPD_PROBE_COMP_GAIN 0x00000040
+#define AR5K_PHY_PAPD_PROBE_TXPOWER 0x00007e00
+#define AR5K_PHY_PAPD_PROBE_TXPOWER_S 9
+#define AR5K_PHY_PAPD_PROBE_TX_NEXT 0x00008000
+#define AR5K_PHY_PAPD_PROBE_PREDIST_EN 0x00010000
+#define AR5K_PHY_PAPD_PROBE_TYPE 0x01800000 /* [5112+] */
+#define AR5K_PHY_PAPD_PROBE_TYPE_S 23
+#define AR5K_PHY_PAPD_PROBE_TYPE_OFDM 0
+#define AR5K_PHY_PAPD_PROBE_TYPE_XR 1
+#define AR5K_PHY_PAPD_PROBE_TYPE_CCK 2
+#define AR5K_PHY_PAPD_PROBE_GAINF 0xfe000000
+#define AR5K_PHY_PAPD_PROBE_GAINF_S 25
+#define AR5K_PHY_PAPD_PROBE_INI_5111 0x00004883 /* [5212+] */
+#define AR5K_PHY_PAPD_PROBE_INI_5112 0x00004882 /* [5212+] */
+
+/*
+ * PHY TX rate power registers [5112+]
+ */
+#define AR5K_PHY_TXPOWER_RATE1 0x9934
+#define AR5K_PHY_TXPOWER_RATE2 0x9938
+#define AR5K_PHY_TXPOWER_RATE_MAX 0x993c
+#define AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE 0x00000040
+#define AR5K_PHY_TXPOWER_RATE3 0xa234
+#define AR5K_PHY_TXPOWER_RATE4 0xa238
+
+/*
+ * PHY frame control register [5111+]
+ */
+#define AR5K_PHY_FRAME_CTL_5210 0x9804
+#define AR5K_PHY_FRAME_CTL_5211 0x9944
+#define AR5K_PHY_FRAME_CTL (ah->ah_version == AR5K_AR5210 ? \
+ AR5K_PHY_FRAME_CTL_5210 : AR5K_PHY_FRAME_CTL_5211)
+/*---[5111+]---*/
+#define AR5K_PHY_FRAME_CTL_WIN_LEN 0x00000003 /* Force window length (?) */
+#define AR5K_PHY_FRAME_CTL_WIN_LEN_S 0
+#define AR5K_PHY_FRAME_CTL_TX_CLIP 0x00000038 /* Mask for tx clip (?) */
+#define AR5K_PHY_FRAME_CTL_TX_CLIP_S 3
+#define AR5K_PHY_FRAME_CTL_PREP_CHINFO 0x00010000 /* Prepend chan info */
+#define AR5K_PHY_FRAME_CTL_EMU 0x80000000
+#define AR5K_PHY_FRAME_CTL_EMU_S 31
+/*---[5110/5111]---*/
+#define AR5K_PHY_FRAME_CTL_TIMING_ERR 0x01000000 /* PHY timing error */
+#define AR5K_PHY_FRAME_CTL_PARITY_ERR 0x02000000 /* Parity error */
+#define AR5K_PHY_FRAME_CTL_ILLRATE_ERR 0x04000000 /* Illegal rate */
+#define AR5K_PHY_FRAME_CTL_ILLLEN_ERR 0x08000000 /* Illegal length */
+#define AR5K_PHY_FRAME_CTL_SERVICE_ERR 0x20000000
+#define AR5K_PHY_FRAME_CTL_TXURN_ERR 0x40000000 /* TX underrun */
+#define AR5K_PHY_FRAME_CTL_INI \
+ (AR5K_PHY_FRAME_CTL_SERVICE_ERR | \
+ AR5K_PHY_FRAME_CTL_TXURN_ERR | \
+ AR5K_PHY_FRAME_CTL_ILLLEN_ERR | \
+ AR5K_PHY_FRAME_CTL_ILLRATE_ERR | \
+ AR5K_PHY_FRAME_CTL_PARITY_ERR | \
+ AR5K_PHY_FRAME_CTL_TIMING_ERR)
+
+/*
+ * PHY Tx Power adjustment register [5212A+]
+ */
+#define AR5K_PHY_TX_PWR_ADJ 0x994c
+#define AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA 0x00000fc0
+#define AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA_S 6
+#define AR5K_PHY_TX_PWR_ADJ_CCK_PCDAC_INDEX 0x00fc0000
+#define AR5K_PHY_TX_PWR_ADJ_CCK_PCDAC_INDEX_S 18
+
+/*
+ * PHY radar detection register [5111+]
+ */
+#define AR5K_PHY_RADAR 0x9954
+#define AR5K_PHY_RADAR_ENABLE 0x00000001
+#define AR5K_PHY_RADAR_DISABLE 0x00000000
+#define AR5K_PHY_RADAR_INBANDTHR 0x0000003e /* Inband threshold
+ 5-bits, units unknown {0..31}
+ (? MHz ?) */
+#define AR5K_PHY_RADAR_INBANDTHR_S 1
+
+#define AR5K_PHY_RADAR_PRSSI_THR 0x00000fc0 /* Pulse RSSI/SNR threshold
+ 6-bits, dBm range {0..63}
+ in dBm units. */
+#define AR5K_PHY_RADAR_PRSSI_THR_S 6
+
+#define AR5K_PHY_RADAR_PHEIGHT_THR 0x0003f000 /* Pulse height threshold
+ 6-bits, dBm range {0..63}
+ in dBm units. */
+#define AR5K_PHY_RADAR_PHEIGHT_THR_S 12
+
+#define AR5K_PHY_RADAR_RSSI_THR 0x00fc0000 /* Radar RSSI/SNR threshold.
+ 6-bits, dBm range {0..63}
+ in dBm units. */
+#define AR5K_PHY_RADAR_RSSI_THR_S 18
+
+#define AR5K_PHY_RADAR_FIRPWR_THR 0x7f000000 /* Finite Impulse Response
+ filter power out threshold.
+ 7-bits, standard power range
+ {0..127} in 1/2 dBm units. */
+#define AR5K_PHY_RADAR_FIRPWR_THRS 24
+
+/*
+ * PHY antenna switch table registers
+ */
+#define AR5K_PHY_ANT_SWITCH_TABLE_0 0x9960
+#define AR5K_PHY_ANT_SWITCH_TABLE_1 0x9964
+
+/*
+ * PHY Noise floor threshold
+ */
+#define AR5K_PHY_NFTHRES 0x9968
+
+/*
+ * Sigma Delta register (?) [5213]
+ */
+#define AR5K_PHY_SIGMA_DELTA 0x996C
+#define AR5K_PHY_SIGMA_DELTA_ADC_SEL 0x00000003
+#define AR5K_PHY_SIGMA_DELTA_ADC_SEL_S 0
+#define AR5K_PHY_SIGMA_DELTA_FILT2 0x000000f8
+#define AR5K_PHY_SIGMA_DELTA_FILT2_S 3
+#define AR5K_PHY_SIGMA_DELTA_FILT1 0x00001f00
+#define AR5K_PHY_SIGMA_DELTA_FILT1_S 8
+#define AR5K_PHY_SIGMA_DELTA_ADC_CLIP 0x01ffe000
+#define AR5K_PHY_SIGMA_DELTA_ADC_CLIP_S 13
+
+/*
+ * RF restart register [5112+] (?)
+ */
+#define AR5K_PHY_RESTART 0x9970 /* restart */
+#define AR5K_PHY_RESTART_DIV_GC 0x001c0000 /* Fast diversity gc_limit (?) */
+#define AR5K_PHY_RESTART_DIV_GC_S 18
+
+/*
+ * RF Bus access request register (for synth-only channel switching)
+ */
+#define AR5K_PHY_RFBUS_REQ 0x997C
+#define AR5K_PHY_RFBUS_REQ_REQUEST 0x00000001
+
+/*
+ * Spur mitigation masks (?)
+ */
+#define AR5K_PHY_TIMING_7 0x9980
+#define AR5K_PHY_TIMING_8 0x9984
+#define AR5K_PHY_TIMING_8_PILOT_MASK_2 0x000fffff
+#define AR5K_PHY_TIMING_8_PILOT_MASK_2_S 0
+
+#define AR5K_PHY_BIN_MASK2_1 0x9988
+#define AR5K_PHY_BIN_MASK2_2 0x998c
+#define AR5K_PHY_BIN_MASK2_3 0x9990
+
+#define AR5K_PHY_BIN_MASK2_4 0x9994
+#define AR5K_PHY_BIN_MASK2_4_MASK_4 0x00003fff
+#define AR5K_PHY_BIN_MASK2_4_MASK_4_S 0
+
+#define AR5K_PHY_TIMING_9 0x9998
+#define AR5K_PHY_TIMING_10 0x999c
+#define AR5K_PHY_TIMING_10_PILOT_MASK_2 0x000fffff
+#define AR5K_PHY_TIMING_10_PILOT_MASK_2_S 0
+
+/*
+ * Spur mitigation control
+ */
+#define AR5K_PHY_TIMING_11 0x99a0 /* Register address */
+#define AR5K_PHY_TIMING_11_SPUR_DELTA_PHASE 0x000fffff /* Spur delta phase */
+#define AR5K_PHY_TIMING_11_SPUR_DELTA_PHASE_S 0
+#define AR5K_PHY_TIMING_11_SPUR_FREQ_SD 0x3ff00000 /* Freq sigma delta */
+#define AR5K_PHY_TIMING_11_SPUR_FREQ_SD_S 20
+#define AR5K_PHY_TIMING_11_USE_SPUR_IN_AGC 0x40000000 /* Spur filter in AGC detector */
+#define AR5K_PHY_TIMING_11_USE_SPUR_IN_SELFCOR 0x80000000 /* Spur filter in OFDM self correlator */
+
+/*
+ * Gain tables
+ */
+#define AR5K_BB_GAIN_BASE 0x9b00 /* BaseBand Amplifier Gain table base address */
+#define AR5K_BB_GAIN(_n) (AR5K_BB_GAIN_BASE + ((_n) << 2))
+#define AR5K_RF_GAIN_BASE 0x9a00 /* RF Amplifier Gain table base address */
+#define AR5K_RF_GAIN(_n) (AR5K_RF_GAIN_BASE + ((_n) << 2))
+
+/*
+ * PHY timing IQ calibration result register [5111+]
+ */
+#define AR5K_PHY_IQRES_CAL_PWR_I 0x9c10 /* I (Inphase) power value */
+#define AR5K_PHY_IQRES_CAL_PWR_Q 0x9c14 /* Q (Quadrature) power value */
+#define AR5K_PHY_IQRES_CAL_CORR 0x9c18 /* I/Q Correlation */
+
+/*
+ * PHY current RSSI register [5111+]
+ */
+#define AR5K_PHY_CURRENT_RSSI 0x9c1c
+
+/*
+ * PHY RF Bus grant register
+ */
+#define AR5K_PHY_RFBUS_GRANT 0x9c20
+#define AR5K_PHY_RFBUS_GRANT_OK 0x00000001
+
+/*
+ * PHY ADC test register
+ */
+#define AR5K_PHY_ADC_TEST 0x9c24
+#define AR5K_PHY_ADC_TEST_I 0x00000001
+#define AR5K_PHY_ADC_TEST_Q 0x00000200
+
+/*
+ * PHY DAC test register
+ */
+#define AR5K_PHY_DAC_TEST 0x9c28
+#define AR5K_PHY_DAC_TEST_I 0x00000001
+#define AR5K_PHY_DAC_TEST_Q 0x00000200
+
+/*
+ * PHY PTAT register (?)
+ */
+#define AR5K_PHY_PTAT 0x9c2c
+
+/*
+ * PHY Illegal TX rate register [5112+]
+ */
+#define AR5K_PHY_BAD_TX_RATE 0x9c30
+
+/*
+ * PHY SPUR Power register [5112+]
+ */
+#define AR5K_PHY_SPUR_PWR 0x9c34 /* Register Address */
+#define AR5K_PHY_SPUR_PWR_I 0x00000001 /* SPUR Power estimate for I (field) */
+#define AR5K_PHY_SPUR_PWR_Q 0x00000100 /* SPUR Power estimate for Q (field) */
+#define AR5K_PHY_SPUR_PWR_FILT 0x00010000 /* Power with SPUR removed (field) */
+
+/*
+ * PHY Channel status register [5112+] (?)
+ */
+#define AR5K_PHY_CHAN_STATUS 0x9c38
+#define AR5K_PHY_CHAN_STATUS_BT_ACT 0x00000001
+#define AR5K_PHY_CHAN_STATUS_RX_CLR_RAW 0x00000002
+#define AR5K_PHY_CHAN_STATUS_RX_CLR_MAC 0x00000004
+#define AR5K_PHY_CHAN_STATUS_RX_CLR_PAP 0x00000008
+
+/*
+ * Heavy clip enable register
+ */
+#define AR5K_PHY_HEAVY_CLIP_ENABLE 0x99e0
+
+/*
+ * PHY clock sleep registers [5112+]
+ */
+#define AR5K_PHY_SCLOCK 0x99f0
+#define AR5K_PHY_SCLOCK_32MHZ 0x0000000c
+#define AR5K_PHY_SDELAY 0x99f4
+#define AR5K_PHY_SDELAY_32MHZ 0x000000ff
+#define AR5K_PHY_SPENDING 0x99f8
+
+
+/*
+ * PHY PAPD I (power?) table (?)
+ * (92! entries)
+ */
+#define AR5K_PHY_PAPD_I_BASE 0xa000
+#define AR5K_PHY_PAPD_I(_n) (AR5K_PHY_PAPD_I_BASE + ((_n) << 2))
+
+/*
+ * PHY PCDAC TX power table
+ */
+#define AR5K_PHY_PCDAC_TXPOWER_BASE 0xa180
+#define AR5K_PHY_PCDAC_TXPOWER(_n) (AR5K_PHY_PCDAC_TXPOWER_BASE + ((_n) << 2))
+
+/*
+ * PHY mode register [5111+]
+ */
+#define AR5K_PHY_MODE 0x0a200 /* Register Address */
+#define AR5K_PHY_MODE_MOD 0x00000001 /* PHY Modulation bit */
+#define AR5K_PHY_MODE_MOD_OFDM 0
+#define AR5K_PHY_MODE_MOD_CCK 1
+#define AR5K_PHY_MODE_FREQ 0x00000002 /* Freq mode bit */
+#define AR5K_PHY_MODE_FREQ_5GHZ 0
+#define AR5K_PHY_MODE_FREQ_2GHZ 2
+#define AR5K_PHY_MODE_MOD_DYN 0x00000004 /* Enable Dynamic OFDM/CCK mode [5112+] */
+#define AR5K_PHY_MODE_RAD 0x00000008 /* [5212+] */
+#define AR5K_PHY_MODE_RAD_RF5111 0
+#define AR5K_PHY_MODE_RAD_RF5112 8
+#define AR5K_PHY_MODE_XR 0x00000010 /* Enable XR mode [5112+] */
+#define AR5K_PHY_MODE_HALF_RATE 0x00000020 /* Enable Half rate (test) */
+#define AR5K_PHY_MODE_QUARTER_RATE 0x00000040 /* Enable Quarter rat (test) */
+
+/*
+ * PHY CCK transmit control register [5111+ (?)]
+ */
+#define AR5K_PHY_CCKTXCTL 0xa204
+#define AR5K_PHY_CCKTXCTL_WORLD 0x00000000
+#define AR5K_PHY_CCKTXCTL_JAPAN 0x00000010
+#define AR5K_PHY_CCKTXCTL_SCRAMBLER_DIS 0x00000001
+#define AR5K_PHY_CCKTXCTK_DAC_SCALE 0x00000004
+
+/*
+ * PHY CCK Cross-correlator Barker RSSI threshold register [5212+]
+ */
+#define AR5K_PHY_CCK_CROSSCORR 0xa208
+#define AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR 0x0000003f
+#define AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR_S 0
+
+/* Same address is used for antenna diversity activation */
+#define AR5K_PHY_FAST_ANT_DIV 0xa208
+#define AR5K_PHY_FAST_ANT_DIV_EN 0x00002000
+
+/*
+ * PHY 2GHz gain register [5111+]
+ */
+#define AR5K_PHY_GAIN_2GHZ 0xa20c
+#define AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX 0x00fc0000
+#define AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX_S 18
+#define AR5K_PHY_GAIN_2GHZ_INI_5111 0x6480416c
+
+#define AR5K_PHY_CCK_RX_CTL_4 0xa21c
+#define AR5K_PHY_CCK_RX_CTL_4_FREQ_EST_SHORT 0x01f80000
+#define AR5K_PHY_CCK_RX_CTL_4_FREQ_EST_SHORT_S 19
+
+#define AR5K_PHY_DAG_CCK_CTL 0xa228
+#define AR5K_PHY_DAG_CCK_CTL_EN_RSSI_THR 0x00000200
+#define AR5K_PHY_DAG_CCK_CTL_RSSI_THR 0x0001fc00
+#define AR5K_PHY_DAG_CCK_CTL_RSSI_THR_S 10
+
+#define AR5K_PHY_FAST_ADC 0xa24c
+
+#define AR5K_PHY_BLUETOOTH 0xa254
+
+/*
+ * Transmit Power Control register
+ * [2413+]
+ */
+#define AR5K_PHY_TPC_RG1 0xa258
+#define AR5K_PHY_TPC_RG1_NUM_PD_GAIN 0x0000c000
+#define AR5K_PHY_TPC_RG1_NUM_PD_GAIN_S 14
+#define AR5K_PHY_TPC_RG1_PDGAIN_1 0x00030000
+#define AR5K_PHY_TPC_RG1_PDGAIN_1_S 16
+#define AR5K_PHY_TPC_RG1_PDGAIN_2 0x000c0000
+#define AR5K_PHY_TPC_RG1_PDGAIN_2_S 18
+#define AR5K_PHY_TPC_RG1_PDGAIN_3 0x00300000
+#define AR5K_PHY_TPC_RG1_PDGAIN_3_S 20
+
+#define AR5K_PHY_TPC_RG5 0xa26C
+#define AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP 0x0000000F
+#define AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP_S 0
+#define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1 0x000003F0
+#define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1_S 4
+#define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2 0x0000FC00
+#define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2_S 10
+#define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3 0x003F0000
+#define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3_S 16
+#define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4 0x0FC00000
+#define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4_S 22
+
+/*
+ * PHY PDADC Tx power table
+ */
+#define AR5K_PHY_PDADC_TXPOWER_BASE 0xa280
+#define AR5K_PHY_PDADC_TXPOWER(_n) (AR5K_PHY_PDADC_TXPOWER_BASE + ((_n) << 2))
+
+/*
+ * Platform registers for WiSoC
+ */
+#define AR5K_AR5312_RESET 0xbc003020
+#define AR5K_AR5312_RESET_BB0_COLD 0x00000004
+#define AR5K_AR5312_RESET_BB1_COLD 0x00000200
+#define AR5K_AR5312_RESET_WMAC0 0x00002000
+#define AR5K_AR5312_RESET_BB0_WARM 0x00004000
+#define AR5K_AR5312_RESET_WMAC1 0x00020000
+#define AR5K_AR5312_RESET_BB1_WARM 0x00040000
+
+#define AR5K_AR5312_ENABLE 0xbc003080
+#define AR5K_AR5312_ENABLE_WLAN0 0x00000001
+#define AR5K_AR5312_ENABLE_WLAN1 0x00000008
+
+#define AR5K_AR2315_RESET 0xb1000004
+#define AR5K_AR2315_RESET_WMAC 0x00000001
+#define AR5K_AR2315_RESET_BB_WARM 0x00000002
+
+#define AR5K_AR2315_AHB_ARB_CTL 0xb1000008
+#define AR5K_AR2315_AHB_ARB_CTL_WLAN 0x00000002
+
+#define AR5K_AR2315_BYTESWAP 0xb100000c
+#define AR5K_AR2315_BYTESWAP_WMAC 0x00000002
diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c
new file mode 100644
index 0000000..2abac25
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/reset.c
@@ -0,0 +1,1290 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
+ * Copyright (c) 2007-2008 Luis Rodriguez <mcgrof@winlab.rutgers.edu>
+ * Copyright (c) 2007-2008 Pavel Roskin <proski@gnu.org>
+ * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com>
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ */
+
+/*****************************\
+ Reset functions and helpers
+\*****************************/
+
+#include <asm/unaligned.h>
+
+#include <linux/pci.h> /* To determine if a card is pci-e */
+#include <linux/log2.h>
+#include <linux/platform_device.h>
+#include "ath5k.h"
+#include "reg.h"
+#include "debug.h"
+
+
+/******************\
+* Helper functions *
+\******************/
+
+/*
+ * Check if a register write has been completed
+ */
+int ath5k_hw_register_timeout(struct ath5k_hw *ah, u32 reg, u32 flag, u32 val,
+ bool is_set)
+{
+ int i;
+ u32 data;
+
+ for (i = AR5K_TUNE_REGISTER_TIMEOUT; i > 0; i--) {
+ data = ath5k_hw_reg_read(ah, reg);
+ if (is_set && (data & flag))
+ break;
+ else if ((data & flag) == val)
+ break;
+ udelay(15);
+ }
+
+ return (i <= 0) ? -EAGAIN : 0;
+}
+
+
+/*************************\
+* Clock related functions *
+\*************************/
+
+/**
+ * ath5k_hw_htoclock - Translate usec to hw clock units
+ *
+ * @ah: The &struct ath5k_hw
+ * @usec: value in microseconds
+ */
+unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec)
+{
+ struct ath_common *common = ath5k_hw_common(ah);
+ return usec * common->clockrate;
+}
+
+/**
+ * ath5k_hw_clocktoh - Translate hw clock units to usec
+ * @clock: value in hw clock units
+ */
+unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock)
+{
+ struct ath_common *common = ath5k_hw_common(ah);
+ return clock / common->clockrate;
+}
+
+/**
+ * ath5k_hw_init_core_clock - Initialize core clock
+ *
+ * @ah The &struct ath5k_hw
+ *
+ * Initialize core clock parameters (usec, usec32, latencies etc).
+ */
+static void ath5k_hw_init_core_clock(struct ath5k_hw *ah)
+{
+ struct ieee80211_channel *channel = ah->ah_current_channel;
+ struct ath_common *common = ath5k_hw_common(ah);
+ u32 usec_reg, txlat, rxlat, usec, clock, sclock, txf2txs;
+
+ /*
+ * Set core clock frequency
+ */
+ switch (channel->hw_value) {
+ case AR5K_MODE_11A:
+ clock = 40;
+ break;
+ case AR5K_MODE_11B:
+ clock = 22;
+ break;
+ case AR5K_MODE_11G:
+ default:
+ clock = 44;
+ break;
+ }
+
+ /* Use clock multiplier for non-default
+ * bwmode */
+ switch (ah->ah_bwmode) {
+ case AR5K_BWMODE_40MHZ:
+ clock *= 2;
+ break;
+ case AR5K_BWMODE_10MHZ:
+ clock /= 2;
+ break;
+ case AR5K_BWMODE_5MHZ:
+ clock /= 4;
+ break;
+ default:
+ break;
+ }
+
+ common->clockrate = clock;
+
+ /*
+ * Set USEC parameters
+ */
+ /* Set USEC counter on PCU*/
+ usec = clock - 1;
+ usec = AR5K_REG_SM(usec, AR5K_USEC_1);
+
+ /* Set usec duration on DCU */
+ if (ah->ah_version != AR5K_AR5210)
+ AR5K_REG_WRITE_BITS(ah, AR5K_DCU_GBL_IFS_MISC,
+ AR5K_DCU_GBL_IFS_MISC_USEC_DUR,
+ clock);
+
+ /* Set 32MHz USEC counter */
+ if ((ah->ah_radio == AR5K_RF5112) ||
+ (ah->ah_radio == AR5K_RF2413) ||
+ (ah->ah_radio == AR5K_RF5413) ||
+ (ah->ah_radio == AR5K_RF2316) ||
+ (ah->ah_radio == AR5K_RF2317))
+ /* Remain on 40MHz clock ? */
+ sclock = 40 - 1;
+ else
+ sclock = 32 - 1;
+ sclock = AR5K_REG_SM(sclock, AR5K_USEC_32);
+
+ /*
+ * Set tx/rx latencies
+ */
+ usec_reg = ath5k_hw_reg_read(ah, AR5K_USEC_5211);
+ txlat = AR5K_REG_MS(usec_reg, AR5K_USEC_TX_LATENCY_5211);
+ rxlat = AR5K_REG_MS(usec_reg, AR5K_USEC_RX_LATENCY_5211);
+
+ /*
+ * Set default Tx frame to Tx data start delay
+ */
+ txf2txs = AR5K_INIT_TXF2TXD_START_DEFAULT;
+
+ /*
+ * 5210 initvals don't include usec settings
+ * so we need to use magic values here for
+ * tx/rx latencies
+ */
+ if (ah->ah_version == AR5K_AR5210) {
+ /* same for turbo */
+ txlat = AR5K_INIT_TX_LATENCY_5210;
+ rxlat = AR5K_INIT_RX_LATENCY_5210;
+ }
+
+ if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
+ /* 5311 has different tx/rx latency masks
+ * from 5211, since we deal 5311 the same
+ * as 5211 when setting initvals, shift
+ * values here to their proper locations
+ *
+ * Note: Initvals indicate tx/rx/ latencies
+ * are the same for turbo mode */
+ txlat = AR5K_REG_SM(txlat, AR5K_USEC_TX_LATENCY_5210);
+ rxlat = AR5K_REG_SM(rxlat, AR5K_USEC_RX_LATENCY_5210);
+ } else
+ switch (ah->ah_bwmode) {
+ case AR5K_BWMODE_10MHZ:
+ txlat = AR5K_REG_SM(txlat * 2,
+ AR5K_USEC_TX_LATENCY_5211);
+ rxlat = AR5K_REG_SM(AR5K_INIT_RX_LAT_MAX,
+ AR5K_USEC_RX_LATENCY_5211);
+ txf2txs = AR5K_INIT_TXF2TXD_START_DELAY_10MHZ;
+ break;
+ case AR5K_BWMODE_5MHZ:
+ txlat = AR5K_REG_SM(txlat * 4,
+ AR5K_USEC_TX_LATENCY_5211);
+ rxlat = AR5K_REG_SM(AR5K_INIT_RX_LAT_MAX,
+ AR5K_USEC_RX_LATENCY_5211);
+ txf2txs = AR5K_INIT_TXF2TXD_START_DELAY_5MHZ;
+ break;
+ case AR5K_BWMODE_40MHZ:
+ txlat = AR5K_INIT_TX_LAT_MIN;
+ rxlat = AR5K_REG_SM(rxlat / 2,
+ AR5K_USEC_RX_LATENCY_5211);
+ txf2txs = AR5K_INIT_TXF2TXD_START_DEFAULT;
+ break;
+ default:
+ break;
+ }
+
+ usec_reg = (usec | sclock | txlat | rxlat);
+ ath5k_hw_reg_write(ah, usec_reg, AR5K_USEC);
+
+ /* On 5112 set tx frame to tx data start delay */
+ if (ah->ah_radio == AR5K_RF5112) {
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RF_CTL2,
+ AR5K_PHY_RF_CTL2_TXF2TXD_START,
+ txf2txs);
+ }
+}
+
+/*
+ * If there is an external 32KHz crystal available, use it
+ * as ref. clock instead of 32/40MHz clock and baseband clocks
+ * to save power during sleep or restore normal 32/40MHz
+ * operation.
+ *
+ * XXX: When operating on 32KHz certain PHY registers (27 - 31,
+ * 123 - 127) require delay on access.
+ */
+static void ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ u32 scal, spending, sclock;
+
+ /* Only set 32KHz settings if we have an external
+ * 32KHz crystal present */
+ if ((AR5K_EEPROM_HAS32KHZCRYSTAL(ee->ee_misc1) ||
+ AR5K_EEPROM_HAS32KHZCRYSTAL_OLD(ee->ee_misc1)) &&
+ enable) {
+
+ /* 1 usec/cycle */
+ AR5K_REG_WRITE_BITS(ah, AR5K_USEC_5211, AR5K_USEC_32, 1);
+ /* Set up tsf increment on each cycle */
+ AR5K_REG_WRITE_BITS(ah, AR5K_TSF_PARM, AR5K_TSF_PARM_INC, 61);
+
+ /* Set baseband sleep control registers
+ * and sleep control rate */
+ ath5k_hw_reg_write(ah, 0x1f, AR5K_PHY_SCR);
+
+ if ((ah->ah_radio == AR5K_RF5112) ||
+ (ah->ah_radio == AR5K_RF5413) ||
+ (ah->ah_radio == AR5K_RF2316) ||
+ (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
+ spending = 0x14;
+ else
+ spending = 0x18;
+ ath5k_hw_reg_write(ah, spending, AR5K_PHY_SPENDING);
+
+ if ((ah->ah_radio == AR5K_RF5112) ||
+ (ah->ah_radio == AR5K_RF5413) ||
+ (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) {
+ ath5k_hw_reg_write(ah, 0x26, AR5K_PHY_SLMT);
+ ath5k_hw_reg_write(ah, 0x0d, AR5K_PHY_SCAL);
+ ath5k_hw_reg_write(ah, 0x07, AR5K_PHY_SCLOCK);
+ ath5k_hw_reg_write(ah, 0x3f, AR5K_PHY_SDELAY);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG,
+ AR5K_PCICFG_SLEEP_CLOCK_RATE, 0x02);
+ } else {
+ ath5k_hw_reg_write(ah, 0x0a, AR5K_PHY_SLMT);
+ ath5k_hw_reg_write(ah, 0x0c, AR5K_PHY_SCAL);
+ ath5k_hw_reg_write(ah, 0x03, AR5K_PHY_SCLOCK);
+ ath5k_hw_reg_write(ah, 0x20, AR5K_PHY_SDELAY);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG,
+ AR5K_PCICFG_SLEEP_CLOCK_RATE, 0x03);
+ }
+
+ /* Enable sleep clock operation */
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG,
+ AR5K_PCICFG_SLEEP_CLOCK_EN);
+
+ } else {
+
+ /* Disable sleep clock operation and
+ * restore default parameters */
+ AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG,
+ AR5K_PCICFG_SLEEP_CLOCK_EN);
+
+ AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG,
+ AR5K_PCICFG_SLEEP_CLOCK_RATE, 0);
+
+ /* Set DAC/ADC delays */
+ ath5k_hw_reg_write(ah, 0x1f, AR5K_PHY_SCR);
+ ath5k_hw_reg_write(ah, AR5K_PHY_SLMT_32MHZ, AR5K_PHY_SLMT);
+
+ if (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))
+ scal = AR5K_PHY_SCAL_32MHZ_2417;
+ else if (ee->ee_is_hb63)
+ scal = AR5K_PHY_SCAL_32MHZ_HB63;
+ else
+ scal = AR5K_PHY_SCAL_32MHZ;
+ ath5k_hw_reg_write(ah, scal, AR5K_PHY_SCAL);
+
+ ath5k_hw_reg_write(ah, AR5K_PHY_SCLOCK_32MHZ, AR5K_PHY_SCLOCK);
+ ath5k_hw_reg_write(ah, AR5K_PHY_SDELAY_32MHZ, AR5K_PHY_SDELAY);
+
+ if ((ah->ah_radio == AR5K_RF5112) ||
+ (ah->ah_radio == AR5K_RF5413) ||
+ (ah->ah_radio == AR5K_RF2316) ||
+ (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
+ spending = 0x14;
+ else
+ spending = 0x18;
+ ath5k_hw_reg_write(ah, spending, AR5K_PHY_SPENDING);
+
+ /* Set up tsf increment on each cycle */
+ AR5K_REG_WRITE_BITS(ah, AR5K_TSF_PARM, AR5K_TSF_PARM_INC, 1);
+
+ if ((ah->ah_radio == AR5K_RF5112) ||
+ (ah->ah_radio == AR5K_RF5413) ||
+ (ah->ah_radio == AR5K_RF2316) ||
+ (ah->ah_radio == AR5K_RF2317))
+ sclock = 40 - 1;
+ else
+ sclock = 32 - 1;
+ AR5K_REG_WRITE_BITS(ah, AR5K_USEC_5211, AR5K_USEC_32, sclock);
+ }
+}
+
+
+/*********************\
+* Reset/Sleep control *
+\*********************/
+
+/*
+ * Reset chipset
+ */
+static int ath5k_hw_nic_reset(struct ath5k_hw *ah, u32 val)
+{
+ int ret;
+ u32 mask = val ? val : ~0U;
+
+ /* Read-and-clear RX Descriptor Pointer*/
+ ath5k_hw_reg_read(ah, AR5K_RXDP);
+
+ /*
+ * Reset the device and wait until success
+ */
+ ath5k_hw_reg_write(ah, val, AR5K_RESET_CTL);
+
+ /* Wait at least 128 PCI clocks */
+ udelay(15);
+
+ if (ah->ah_version == AR5K_AR5210) {
+ val &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_DMA
+ | AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_PHY;
+ mask &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_DMA
+ | AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_PHY;
+ } else {
+ val &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND;
+ mask &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND;
+ }
+
+ ret = ath5k_hw_register_timeout(ah, AR5K_RESET_CTL, mask, val, false);
+
+ /*
+ * Reset configuration register (for hw byte-swap). Note that this
+ * is only set for big endian. We do the necessary magic in
+ * AR5K_INIT_CFG.
+ */
+ if ((val & AR5K_RESET_CTL_PCU) == 0)
+ ath5k_hw_reg_write(ah, AR5K_INIT_CFG, AR5K_CFG);
+
+ return ret;
+}
+
+/*
+ * Reset AHB chipset
+ * AR5K_RESET_CTL_PCU flag resets WMAC
+ * AR5K_RESET_CTL_BASEBAND flag resets WBB
+ */
+static int ath5k_hw_wisoc_reset(struct ath5k_hw *ah, u32 flags)
+{
+ u32 mask = flags ? flags : ~0U;
+ u32 __iomem *reg;
+ u32 regval;
+ u32 val = 0;
+
+ /* ah->ah_mac_srev is not available at this point yet */
+ if (ah->devid >= AR5K_SREV_AR2315_R6) {
+ reg = (u32 __iomem *) AR5K_AR2315_RESET;
+ if (mask & AR5K_RESET_CTL_PCU)
+ val |= AR5K_AR2315_RESET_WMAC;
+ if (mask & AR5K_RESET_CTL_BASEBAND)
+ val |= AR5K_AR2315_RESET_BB_WARM;
+ } else {
+ reg = (u32 __iomem *) AR5K_AR5312_RESET;
+ if (to_platform_device(ah->dev)->id == 0) {
+ if (mask & AR5K_RESET_CTL_PCU)
+ val |= AR5K_AR5312_RESET_WMAC0;
+ if (mask & AR5K_RESET_CTL_BASEBAND)
+ val |= AR5K_AR5312_RESET_BB0_COLD |
+ AR5K_AR5312_RESET_BB0_WARM;
+ } else {
+ if (mask & AR5K_RESET_CTL_PCU)
+ val |= AR5K_AR5312_RESET_WMAC1;
+ if (mask & AR5K_RESET_CTL_BASEBAND)
+ val |= AR5K_AR5312_RESET_BB1_COLD |
+ AR5K_AR5312_RESET_BB1_WARM;
+ }
+ }
+
+ /* Put BB/MAC into reset */
+ regval = __raw_readl(reg);
+ __raw_writel(regval | val, reg);
+ regval = __raw_readl(reg);
+ udelay(100);
+
+ /* Bring BB/MAC out of reset */
+ __raw_writel(regval & ~val, reg);
+ regval = __raw_readl(reg);
+
+ /*
+ * Reset configuration register (for hw byte-swap). Note that this
+ * is only set for big endian. We do the necessary magic in
+ * AR5K_INIT_CFG.
+ */
+ if ((flags & AR5K_RESET_CTL_PCU) == 0)
+ ath5k_hw_reg_write(ah, AR5K_INIT_CFG, AR5K_CFG);
+
+ return 0;
+}
+
+
+/*
+ * Sleep control
+ */
+static int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode,
+ bool set_chip, u16 sleep_duration)
+{
+ unsigned int i;
+ u32 staid, data;
+
+ staid = ath5k_hw_reg_read(ah, AR5K_STA_ID1);
+
+ switch (mode) {
+ case AR5K_PM_AUTO:
+ staid &= ~AR5K_STA_ID1_DEFAULT_ANTENNA;
+ /* fallthrough */
+ case AR5K_PM_NETWORK_SLEEP:
+ if (set_chip)
+ ath5k_hw_reg_write(ah,
+ AR5K_SLEEP_CTL_SLE_ALLOW |
+ sleep_duration,
+ AR5K_SLEEP_CTL);
+
+ staid |= AR5K_STA_ID1_PWR_SV;
+ break;
+
+ case AR5K_PM_FULL_SLEEP:
+ if (set_chip)
+ ath5k_hw_reg_write(ah, AR5K_SLEEP_CTL_SLE_SLP,
+ AR5K_SLEEP_CTL);
+
+ staid |= AR5K_STA_ID1_PWR_SV;
+ break;
+
+ case AR5K_PM_AWAKE:
+
+ staid &= ~AR5K_STA_ID1_PWR_SV;
+
+ if (!set_chip)
+ goto commit;
+
+ data = ath5k_hw_reg_read(ah, AR5K_SLEEP_CTL);
+
+ /* If card is down we 'll get 0xffff... so we
+ * need to clean this up before we write the register
+ */
+ if (data & 0xffc00000)
+ data = 0;
+ else
+ /* Preserve sleep duration etc */
+ data = data & ~AR5K_SLEEP_CTL_SLE;
+
+ ath5k_hw_reg_write(ah, data | AR5K_SLEEP_CTL_SLE_WAKE,
+ AR5K_SLEEP_CTL);
+ udelay(15);
+
+ for (i = 200; i > 0; i--) {
+ /* Check if the chip did wake up */
+ if ((ath5k_hw_reg_read(ah, AR5K_PCICFG) &
+ AR5K_PCICFG_SPWR_DN) == 0)
+ break;
+
+ /* Wait a bit and retry */
+ udelay(50);
+ ath5k_hw_reg_write(ah, data | AR5K_SLEEP_CTL_SLE_WAKE,
+ AR5K_SLEEP_CTL);
+ }
+
+ /* Fail if the chip didn't wake up */
+ if (i == 0)
+ return -EIO;
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+commit:
+ ath5k_hw_reg_write(ah, staid, AR5K_STA_ID1);
+
+ return 0;
+}
+
+/*
+ * Put device on hold
+ *
+ * Put MAC and Baseband on warm reset and
+ * keep that state (don't clean sleep control
+ * register). After this MAC and Baseband are
+ * disabled and a full reset is needed to come
+ * back. This way we save as much power as possible
+ * without putting the card on full sleep.
+ */
+int ath5k_hw_on_hold(struct ath5k_hw *ah)
+{
+ struct pci_dev *pdev = ah->pdev;
+ u32 bus_flags;
+ int ret;
+
+ if (ath5k_get_bus_type(ah) == ATH_AHB)
+ return 0;
+
+ /* Make sure device is awake */
+ ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
+ if (ret) {
+ ATH5K_ERR(ah, "failed to wakeup the MAC Chip\n");
+ return ret;
+ }
+
+ /*
+ * Put chipset on warm reset...
+ *
+ * Note: putting PCI core on warm reset on PCI-E cards
+ * results card to hang and always return 0xffff... so
+ * we ignore that flag for PCI-E cards. On PCI cards
+ * this flag gets cleared after 64 PCI clocks.
+ */
+ bus_flags = (pdev && pci_is_pcie(pdev)) ? 0 : AR5K_RESET_CTL_PCI;
+
+ if (ah->ah_version == AR5K_AR5210) {
+ ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
+ AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA |
+ AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI);
+ mdelay(2);
+ } else {
+ ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
+ AR5K_RESET_CTL_BASEBAND | bus_flags);
+ }
+
+ if (ret) {
+ ATH5K_ERR(ah, "failed to put device on warm reset\n");
+ return -EIO;
+ }
+
+ /* ...wakeup again!*/
+ ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
+ if (ret) {
+ ATH5K_ERR(ah, "failed to put device on hold\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+/*
+ * Bring up MAC + PHY Chips and program PLL
+ * Channel is NULL for the initial wakeup.
+ */
+int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel)
+{
+ struct pci_dev *pdev = ah->pdev;
+ u32 turbo, mode, clock, bus_flags;
+ int ret;
+
+ turbo = 0;
+ mode = 0;
+ clock = 0;
+
+ if ((ath5k_get_bus_type(ah) != ATH_AHB) || channel) {
+ /* Wakeup the device */
+ ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
+ if (ret) {
+ ATH5K_ERR(ah, "failed to wakeup the MAC Chip\n");
+ return ret;
+ }
+ }
+
+ /*
+ * Put chipset on warm reset...
+ *
+ * Note: putting PCI core on warm reset on PCI-E cards
+ * results card to hang and always return 0xffff... so
+ * we ignore that flag for PCI-E cards. On PCI cards
+ * this flag gets cleared after 64 PCI clocks.
+ */
+ bus_flags = (pdev && pci_is_pcie(pdev)) ? 0 : AR5K_RESET_CTL_PCI;
+
+ if (ah->ah_version == AR5K_AR5210) {
+ ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
+ AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA |
+ AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI);
+ mdelay(2);
+ } else {
+ if (ath5k_get_bus_type(ah) == ATH_AHB)
+ ret = ath5k_hw_wisoc_reset(ah, AR5K_RESET_CTL_PCU |
+ AR5K_RESET_CTL_BASEBAND);
+ else
+ ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
+ AR5K_RESET_CTL_BASEBAND | bus_flags);
+ }
+
+ if (ret) {
+ ATH5K_ERR(ah, "failed to reset the MAC Chip\n");
+ return -EIO;
+ }
+
+ /* ...wakeup again!...*/
+ ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
+ if (ret) {
+ ATH5K_ERR(ah, "failed to resume the MAC Chip\n");
+ return ret;
+ }
+
+ /* ...reset configuration register on Wisoc ...
+ * ...clear reset control register and pull device out of
+ * warm reset on others */
+ if (ath5k_get_bus_type(ah) == ATH_AHB)
+ ret = ath5k_hw_wisoc_reset(ah, 0);
+ else
+ ret = ath5k_hw_nic_reset(ah, 0);
+
+ if (ret) {
+ ATH5K_ERR(ah, "failed to warm reset the MAC Chip\n");
+ return -EIO;
+ }
+
+ /* On initialization skip PLL programming since we don't have
+ * a channel / mode set yet */
+ if (!channel)
+ return 0;
+
+ if (ah->ah_version != AR5K_AR5210) {
+ /*
+ * Get channel mode flags
+ */
+
+ if (ah->ah_radio >= AR5K_RF5112) {
+ mode = AR5K_PHY_MODE_RAD_RF5112;
+ clock = AR5K_PHY_PLL_RF5112;
+ } else {
+ mode = AR5K_PHY_MODE_RAD_RF5111; /*Zero*/
+ clock = AR5K_PHY_PLL_RF5111; /*Zero*/
+ }
+
+ if (channel->band == IEEE80211_BAND_2GHZ) {
+ mode |= AR5K_PHY_MODE_FREQ_2GHZ;
+ clock |= AR5K_PHY_PLL_44MHZ;
+
+ if (channel->hw_value == AR5K_MODE_11B) {
+ mode |= AR5K_PHY_MODE_MOD_CCK;
+ } else {
+ /* XXX Dynamic OFDM/CCK is not supported by the
+ * AR5211 so we set MOD_OFDM for plain g (no
+ * CCK headers) operation. We need to test
+ * this, 5211 might support ofdm-only g after
+ * all, there are also initial register values
+ * in the code for g mode (see initvals.c).
+ */
+ if (ah->ah_version == AR5K_AR5211)
+ mode |= AR5K_PHY_MODE_MOD_OFDM;
+ else
+ mode |= AR5K_PHY_MODE_MOD_DYN;
+ }
+ } else if (channel->band == IEEE80211_BAND_5GHZ) {
+ mode |= (AR5K_PHY_MODE_FREQ_5GHZ |
+ AR5K_PHY_MODE_MOD_OFDM);
+
+ /* Different PLL setting for 5413 */
+ if (ah->ah_radio == AR5K_RF5413)
+ clock = AR5K_PHY_PLL_40MHZ_5413;
+ else
+ clock |= AR5K_PHY_PLL_40MHZ;
+ } else {
+ ATH5K_ERR(ah, "invalid radio frequency mode\n");
+ return -EINVAL;
+ }
+
+ /*XXX: Can bwmode be used with dynamic mode ?
+ * (I don't think it supports 44MHz) */
+ /* On 2425 initvals TURBO_SHORT is not present */
+ if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) {
+ turbo = AR5K_PHY_TURBO_MODE |
+ (ah->ah_radio == AR5K_RF2425) ? 0 :
+ AR5K_PHY_TURBO_SHORT;
+ } else if (ah->ah_bwmode != AR5K_BWMODE_DEFAULT) {
+ if (ah->ah_radio == AR5K_RF5413) {
+ mode |= (ah->ah_bwmode == AR5K_BWMODE_10MHZ) ?
+ AR5K_PHY_MODE_HALF_RATE :
+ AR5K_PHY_MODE_QUARTER_RATE;
+ } else if (ah->ah_version == AR5K_AR5212) {
+ clock |= (ah->ah_bwmode == AR5K_BWMODE_10MHZ) ?
+ AR5K_PHY_PLL_HALF_RATE :
+ AR5K_PHY_PLL_QUARTER_RATE;
+ }
+ }
+
+ } else { /* Reset the device */
+
+ /* ...enable Atheros turbo mode if requested */
+ if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
+ ath5k_hw_reg_write(ah, AR5K_PHY_TURBO_MODE,
+ AR5K_PHY_TURBO);
+ }
+
+ if (ah->ah_version != AR5K_AR5210) {
+
+ /* ...update PLL if needed */
+ if (ath5k_hw_reg_read(ah, AR5K_PHY_PLL) != clock) {
+ ath5k_hw_reg_write(ah, clock, AR5K_PHY_PLL);
+ udelay(300);
+ }
+
+ /* ...set the PHY operating mode */
+ ath5k_hw_reg_write(ah, mode, AR5K_PHY_MODE);
+ ath5k_hw_reg_write(ah, turbo, AR5K_PHY_TURBO);
+ }
+
+ return 0;
+}
+
+
+/**************************************\
+* Post-initvals register modifications *
+\**************************************/
+
+/* TODO: Half/Quarter rate */
+static void ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel)
+{
+ if (ah->ah_version == AR5K_AR5212 &&
+ ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
+
+ /* Setup ADC control */
+ ath5k_hw_reg_write(ah,
+ (AR5K_REG_SM(2,
+ AR5K_PHY_ADC_CTL_INBUFGAIN_OFF) |
+ AR5K_REG_SM(2,
+ AR5K_PHY_ADC_CTL_INBUFGAIN_ON) |
+ AR5K_PHY_ADC_CTL_PWD_DAC_OFF |
+ AR5K_PHY_ADC_CTL_PWD_ADC_OFF),
+ AR5K_PHY_ADC_CTL);
+
+
+
+ /* Disable barker RSSI threshold */
+ AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_DAG_CCK_CTL,
+ AR5K_PHY_DAG_CCK_CTL_EN_RSSI_THR);
+
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DAG_CCK_CTL,
+ AR5K_PHY_DAG_CCK_CTL_RSSI_THR, 2);
+
+ /* Set the mute mask */
+ ath5k_hw_reg_write(ah, 0x0000000f, AR5K_SEQ_MASK);
+ }
+
+ /* Clear PHY_BLUETOOTH to allow RX_CLEAR line debug */
+ if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212B)
+ ath5k_hw_reg_write(ah, 0, AR5K_PHY_BLUETOOTH);
+
+ /* Enable DCU double buffering */
+ if (ah->ah_phy_revision > AR5K_SREV_PHY_5212B)
+ AR5K_REG_DISABLE_BITS(ah, AR5K_TXCFG,
+ AR5K_TXCFG_DCU_DBL_BUF_DIS);
+
+ /* Set fast ADC */
+ if ((ah->ah_radio == AR5K_RF5413) ||
+ (ah->ah_radio == AR5K_RF2317) ||
+ (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) {
+ u32 fast_adc = true;
+
+ if (channel->center_freq == 2462 ||
+ channel->center_freq == 2467)
+ fast_adc = 0;
+
+ /* Only update if needed */
+ if (ath5k_hw_reg_read(ah, AR5K_PHY_FAST_ADC) != fast_adc)
+ ath5k_hw_reg_write(ah, fast_adc,
+ AR5K_PHY_FAST_ADC);
+ }
+
+ /* Fix for first revision of the RF5112 RF chipset */
+ if (ah->ah_radio == AR5K_RF5112 &&
+ ah->ah_radio_5ghz_revision <
+ AR5K_SREV_RAD_5112A) {
+ u32 data;
+ ath5k_hw_reg_write(ah, AR5K_PHY_CCKTXCTL_WORLD,
+ AR5K_PHY_CCKTXCTL);
+ if (channel->band == IEEE80211_BAND_5GHZ)
+ data = 0xffb81020;
+ else
+ data = 0xffb80d20;
+ ath5k_hw_reg_write(ah, data, AR5K_PHY_FRAME_CTL);
+ }
+
+ if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
+ /* Clear QCU/DCU clock gating register */
+ ath5k_hw_reg_write(ah, 0, AR5K_QCUDCU_CLKGT);
+ /* Set DAC/ADC delays */
+ ath5k_hw_reg_write(ah, AR5K_PHY_SCAL_32MHZ_5311,
+ AR5K_PHY_SCAL);
+ /* Enable PCU FIFO corruption ECO */
+ AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211,
+ AR5K_DIAG_SW_ECO_ENABLE);
+ }
+
+ if (ah->ah_bwmode) {
+ /* Increase PHY switch and AGC settling time
+ * on turbo mode (ath5k_hw_commit_eeprom_settings
+ * will override settling time if available) */
+ if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) {
+
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING,
+ AR5K_PHY_SETTLING_AGC,
+ AR5K_AGC_SETTLING_TURBO);
+
+ /* XXX: Initvals indicate we only increase
+ * switch time on AR5212, 5211 and 5210
+ * only change agc time (bug?) */
+ if (ah->ah_version == AR5K_AR5212)
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING,
+ AR5K_PHY_SETTLING_SWITCH,
+ AR5K_SWITCH_SETTLING_TURBO);
+
+ if (ah->ah_version == AR5K_AR5210) {
+ /* Set Frame Control Register */
+ ath5k_hw_reg_write(ah,
+ (AR5K_PHY_FRAME_CTL_INI |
+ AR5K_PHY_TURBO_MODE |
+ AR5K_PHY_TURBO_SHORT | 0x2020),
+ AR5K_PHY_FRAME_CTL_5210);
+ }
+ /* On 5413 PHY force window length for half/quarter rate*/
+ } else if ((ah->ah_mac_srev >= AR5K_SREV_AR5424) &&
+ (ah->ah_mac_srev <= AR5K_SREV_AR5414)) {
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_FRAME_CTL_5211,
+ AR5K_PHY_FRAME_CTL_WIN_LEN,
+ 3);
+ }
+ } else if (ah->ah_version == AR5K_AR5210) {
+ /* Set Frame Control Register for normal operation */
+ ath5k_hw_reg_write(ah, (AR5K_PHY_FRAME_CTL_INI | 0x1020),
+ AR5K_PHY_FRAME_CTL_5210);
+ }
+}
+
+static void ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ s16 cck_ofdm_pwr_delta;
+ u8 ee_mode;
+
+ /* TODO: Add support for AR5210 EEPROM */
+ if (ah->ah_version == AR5K_AR5210)
+ return;
+
+ ee_mode = ath5k_eeprom_mode_from_channel(channel);
+
+ /* Adjust power delta for channel 14 */
+ if (channel->center_freq == 2484)
+ cck_ofdm_pwr_delta =
+ ((ee->ee_cck_ofdm_power_delta -
+ ee->ee_scaled_cck_delta) * 2) / 10;
+ else
+ cck_ofdm_pwr_delta =
+ (ee->ee_cck_ofdm_power_delta * 2) / 10;
+
+ /* Set CCK to OFDM power delta on tx power
+ * adjustment register */
+ if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
+ if (channel->hw_value == AR5K_MODE_11G)
+ ath5k_hw_reg_write(ah,
+ AR5K_REG_SM((ee->ee_cck_ofdm_gain_delta * -1),
+ AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA) |
+ AR5K_REG_SM((cck_ofdm_pwr_delta * -1),
+ AR5K_PHY_TX_PWR_ADJ_CCK_PCDAC_INDEX),
+ AR5K_PHY_TX_PWR_ADJ);
+ else
+ ath5k_hw_reg_write(ah, 0, AR5K_PHY_TX_PWR_ADJ);
+ } else {
+ /* For older revs we scale power on sw during tx power
+ * setup */
+ ah->ah_txpower.txp_cck_ofdm_pwr_delta = cck_ofdm_pwr_delta;
+ ah->ah_txpower.txp_cck_ofdm_gainf_delta =
+ ee->ee_cck_ofdm_gain_delta;
+ }
+
+ /* XXX: necessary here? is called from ath5k_hw_set_antenna_mode()
+ * too */
+ ath5k_hw_set_antenna_switch(ah, ee_mode);
+
+ /* Noise floor threshold */
+ ath5k_hw_reg_write(ah,
+ AR5K_PHY_NF_SVAL(ee->ee_noise_floor_thr[ee_mode]),
+ AR5K_PHY_NFTHRES);
+
+ if ((ah->ah_bwmode == AR5K_BWMODE_40MHZ) &&
+ (ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_0)) {
+ /* Switch settling time (Turbo) */
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING,
+ AR5K_PHY_SETTLING_SWITCH,
+ ee->ee_switch_settling_turbo[ee_mode]);
+
+ /* Tx/Rx attenuation (Turbo) */
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN,
+ AR5K_PHY_GAIN_TXRX_ATTEN,
+ ee->ee_atn_tx_rx_turbo[ee_mode]);
+
+ /* ADC/PGA desired size (Turbo) */
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE,
+ AR5K_PHY_DESIRED_SIZE_ADC,
+ ee->ee_adc_desired_size_turbo[ee_mode]);
+
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE,
+ AR5K_PHY_DESIRED_SIZE_PGA,
+ ee->ee_pga_desired_size_turbo[ee_mode]);
+
+ /* Tx/Rx margin (Turbo) */
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN_2GHZ,
+ AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX,
+ ee->ee_margin_tx_rx_turbo[ee_mode]);
+
+ } else {
+ /* Switch settling time */
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING,
+ AR5K_PHY_SETTLING_SWITCH,
+ ee->ee_switch_settling[ee_mode]);
+
+ /* Tx/Rx attenuation */
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN,
+ AR5K_PHY_GAIN_TXRX_ATTEN,
+ ee->ee_atn_tx_rx[ee_mode]);
+
+ /* ADC/PGA desired size */
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE,
+ AR5K_PHY_DESIRED_SIZE_ADC,
+ ee->ee_adc_desired_size[ee_mode]);
+
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE,
+ AR5K_PHY_DESIRED_SIZE_PGA,
+ ee->ee_pga_desired_size[ee_mode]);
+
+ /* Tx/Rx margin */
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1)
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN_2GHZ,
+ AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX,
+ ee->ee_margin_tx_rx[ee_mode]);
+ }
+
+ /* XPA delays */
+ ath5k_hw_reg_write(ah,
+ (ee->ee_tx_end2xpa_disable[ee_mode] << 24) |
+ (ee->ee_tx_end2xpa_disable[ee_mode] << 16) |
+ (ee->ee_tx_frm2xpa_enable[ee_mode] << 8) |
+ (ee->ee_tx_frm2xpa_enable[ee_mode]), AR5K_PHY_RF_CTL4);
+
+ /* XLNA delay */
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RF_CTL3,
+ AR5K_PHY_RF_CTL3_TXE2XLNA_ON,
+ ee->ee_tx_end2xlna_enable[ee_mode]);
+
+ /* Thresh64 (ANI) */
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_NF,
+ AR5K_PHY_NF_THRESH62,
+ ee->ee_thr_62[ee_mode]);
+
+ /* False detect backoff for channels
+ * that have spur noise. Write the new
+ * cyclic power RSSI threshold. */
+ if (ath5k_hw_chan_has_spur_noise(ah, channel))
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR,
+ AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1,
+ AR5K_INIT_CYCRSSI_THR1 +
+ ee->ee_false_detect[ee_mode]);
+ else
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR,
+ AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1,
+ AR5K_INIT_CYCRSSI_THR1);
+
+ /* I/Q correction (set enable bit last to match HAL sources) */
+ /* TODO: Per channel i/q infos ? */
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) {
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_I_COFF,
+ ee->ee_i_cal[ee_mode]);
+ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_Q_COFF,
+ ee->ee_q_cal[ee_mode]);
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE);
+ }
+
+ /* Heavy clipping -disable for now */
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_1)
+ ath5k_hw_reg_write(ah, 0, AR5K_PHY_HEAVY_CLIP_ENABLE);
+}
+
+
+/*********************\
+* Main reset function *
+\*********************/
+
+int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
+ struct ieee80211_channel *channel, bool fast, bool skip_pcu)
+{
+ u32 s_seq[10], s_led[3], tsf_up, tsf_lo;
+ u8 mode;
+ int i, ret;
+
+ tsf_up = 0;
+ tsf_lo = 0;
+ mode = 0;
+
+ /*
+ * Sanity check for fast flag
+ * Fast channel change only available
+ * on AR2413/AR5413.
+ */
+ if (fast && (ah->ah_radio != AR5K_RF2413) &&
+ (ah->ah_radio != AR5K_RF5413))
+ fast = 0;
+
+ /* Disable sleep clock operation
+ * to avoid register access delay on certain
+ * PHY registers */
+ if (ah->ah_version == AR5K_AR5212)
+ ath5k_hw_set_sleep_clock(ah, false);
+
+ /*
+ * Stop PCU
+ */
+ ath5k_hw_stop_rx_pcu(ah);
+
+ /*
+ * Stop DMA
+ *
+ * Note: If DMA didn't stop continue
+ * since only a reset will fix it.
+ */
+ ret = ath5k_hw_dma_stop(ah);
+
+ /* RF Bus grant won't work if we have pending
+ * frames */
+ if (ret && fast) {
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
+ "DMA didn't stop, falling back to normal reset\n");
+ fast = 0;
+ /* Non fatal, just continue with
+ * normal reset */
+ ret = 0;
+ }
+
+ mode = channel->hw_value;
+ switch (mode) {
+ case AR5K_MODE_11A:
+ break;
+ case AR5K_MODE_11G:
+ if (ah->ah_version <= AR5K_AR5211) {
+ ATH5K_ERR(ah,
+ "G mode not available on 5210/5211");
+ return -EINVAL;
+ }
+ break;
+ case AR5K_MODE_11B:
+ if (ah->ah_version < AR5K_AR5211) {
+ ATH5K_ERR(ah,
+ "B mode not available on 5210");
+ return -EINVAL;
+ }
+ break;
+ default:
+ ATH5K_ERR(ah,
+ "invalid channel: %d\n", channel->center_freq);
+ return -EINVAL;
+ }
+
+ /*
+ * If driver requested fast channel change and DMA has stopped
+ * go on. If it fails continue with a normal reset.
+ */
+ if (fast) {
+ ret = ath5k_hw_phy_init(ah, channel, mode, true);
+ if (ret) {
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
+ "fast chan change failed, falling back to normal reset\n");
+ /* Non fatal, can happen eg.
+ * on mode change */
+ ret = 0;
+ } else {
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
+ "fast chan change successful\n");
+ return 0;
+ }
+ }
+
+ /*
+ * Save some registers before a reset
+ */
+ if (ah->ah_version != AR5K_AR5210) {
+ /*
+ * Save frame sequence count
+ * For revs. after Oahu, only save
+ * seq num for DCU 0 (Global seq num)
+ */
+ if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
+
+ for (i = 0; i < 10; i++)
+ s_seq[i] = ath5k_hw_reg_read(ah,
+ AR5K_QUEUE_DCU_SEQNUM(i));
+
+ } else {
+ s_seq[0] = ath5k_hw_reg_read(ah,
+ AR5K_QUEUE_DCU_SEQNUM(0));
+ }
+
+ /* TSF accelerates on AR5211 during reset
+ * As a workaround save it here and restore
+ * it later so that it's back in time after
+ * reset. This way it'll get re-synced on the
+ * next beacon without breaking ad-hoc.
+ *
+ * On AR5212 TSF is almost preserved across a
+ * reset so it stays back in time anyway and
+ * we don't have to save/restore it.
+ *
+ * XXX: Since this breaks power saving we have
+ * to disable power saving until we receive the
+ * next beacon, so we can resync beacon timers */
+ if (ah->ah_version == AR5K_AR5211) {
+ tsf_up = ath5k_hw_reg_read(ah, AR5K_TSF_U32);
+ tsf_lo = ath5k_hw_reg_read(ah, AR5K_TSF_L32);
+ }
+ }
+
+
+ /*GPIOs*/
+ s_led[0] = ath5k_hw_reg_read(ah, AR5K_PCICFG) &
+ AR5K_PCICFG_LEDSTATE;
+ s_led[1] = ath5k_hw_reg_read(ah, AR5K_GPIOCR);
+ s_led[2] = ath5k_hw_reg_read(ah, AR5K_GPIODO);
+
+
+ /*
+ * Since we are going to write rf buffer
+ * check if we have any pending gain_F
+ * optimization settings
+ */
+ if (ah->ah_version == AR5K_AR5212 &&
+ (ah->ah_radio <= AR5K_RF5112)) {
+ if (!fast && ah->ah_rf_banks != NULL)
+ ath5k_hw_gainf_calibrate(ah);
+ }
+
+ /* Wakeup the device */
+ ret = ath5k_hw_nic_wakeup(ah, channel);
+ if (ret)
+ return ret;
+
+ /* PHY access enable */
+ if (ah->ah_mac_srev >= AR5K_SREV_AR5211)
+ ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
+ else
+ ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ | 0x40,
+ AR5K_PHY(0));
+
+ /* Write initial settings */
+ ret = ath5k_hw_write_initvals(ah, mode, skip_pcu);
+ if (ret)
+ return ret;
+
+ /* Initialize core clock settings */
+ ath5k_hw_init_core_clock(ah);
+
+ /*
+ * Tweak initval settings for revised
+ * chipsets and add some more config
+ * bits
+ */
+ ath5k_hw_tweak_initval_settings(ah, channel);
+
+ /* Commit values from EEPROM */
+ ath5k_hw_commit_eeprom_settings(ah, channel);
+
+
+ /*
+ * Restore saved values
+ */
+
+ /* Seqnum, TSF */
+ if (ah->ah_version != AR5K_AR5210) {
+ if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
+ for (i = 0; i < 10; i++)
+ ath5k_hw_reg_write(ah, s_seq[i],
+ AR5K_QUEUE_DCU_SEQNUM(i));
+ } else {
+ ath5k_hw_reg_write(ah, s_seq[0],
+ AR5K_QUEUE_DCU_SEQNUM(0));
+ }
+
+ if (ah->ah_version == AR5K_AR5211) {
+ ath5k_hw_reg_write(ah, tsf_up, AR5K_TSF_U32);
+ ath5k_hw_reg_write(ah, tsf_lo, AR5K_TSF_L32);
+ }
+ }
+
+ /* Ledstate */
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, s_led[0]);
+
+ /* Gpio settings */
+ ath5k_hw_reg_write(ah, s_led[1], AR5K_GPIOCR);
+ ath5k_hw_reg_write(ah, s_led[2], AR5K_GPIODO);
+
+ /*
+ * Initialize PCU
+ */
+ ath5k_hw_pcu_init(ah, op_mode, mode);
+
+ /*
+ * Initialize PHY
+ */
+ ret = ath5k_hw_phy_init(ah, channel, mode, false);
+ if (ret) {
+ ATH5K_ERR(ah,
+ "failed to initialize PHY (%i) !\n", ret);
+ return ret;
+ }
+
+ /*
+ * Configure QCUs/DCUs
+ */
+ ret = ath5k_hw_init_queues(ah);
+ if (ret)
+ return ret;
+
+
+ /*
+ * Initialize DMA/Interrupts
+ */
+ ath5k_hw_dma_init(ah);
+
+
+ /*
+ * Enable 32KHz clock function for AR5212+ chips
+ * Set clocks to 32KHz operation and use an
+ * external 32KHz crystal when sleeping if one
+ * exists.
+ * Disabled by default because it is also disabled in
+ * other drivers and it is known to cause stability
+ * issues on some devices
+ */
+ if (ah->ah_use_32khz_clock && ah->ah_version == AR5K_AR5212 &&
+ op_mode != NL80211_IFTYPE_AP)
+ ath5k_hw_set_sleep_clock(ah, true);
+
+ /*
+ * Disable beacons and reset the TSF
+ */
+ AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE);
+ ath5k_hw_reset_tsf(ah);
+ return 0;
+}
diff --git a/drivers/net/wireless/ath/ath5k/rfbuffer.h b/drivers/net/wireless/ath/ath5k/rfbuffer.h
new file mode 100644
index 0000000..5d11c23
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/rfbuffer.h
@@ -0,0 +1,828 @@
+/*
+ * RF Buffer handling functions
+ *
+ * Copyright (c) 2009 Nick Kossifidis <mickflemm@gmail.com>
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ */
+
+
+/*
+ * There are some special registers on the RF chip
+ * that control various operation settings related mostly to
+ * the analog parts (channel, gain adjustment etc).
+ *
+ * We don't write on those registers directly but
+ * we send a data packet on the chip, using a special register,
+ * that holds all the settings we need. After we've sent the
+ * data packet, we write on another special register to notify hw
+ * to apply the settings. This is done so that control registers
+ * can be dynamically programmed during operation and the settings
+ * are applied faster on the hw.
+ *
+ * We call each data packet an "RF Bank" and all the data we write
+ * (all RF Banks) "RF Buffer". This file holds initial RF Buffer
+ * data for the different RF chips, and various info to match RF
+ * Buffer offsets with specific RF registers so that we can access
+ * them. We tweak these settings on rfregs_init function.
+ *
+ * Also check out reg.h and U.S. Patent 6677779 B1 (about buffer
+ * registers and control registers):
+ *
+ * http://www.google.com/patents?id=qNURAAAAEBAJ
+ */
+
+
+/*
+ * Struct to hold default mode specific RF
+ * register values (RF Banks)
+ */
+struct ath5k_ini_rfbuffer {
+ u8 rfb_bank; /* RF Bank number */
+ u16 rfb_ctrl_register; /* RF Buffer control register */
+ u32 rfb_mode_data[3]; /* RF Buffer data for each mode */
+};
+
+/*
+ * Struct to hold RF Buffer field
+ * infos used to access certain RF
+ * analog registers
+ */
+struct ath5k_rfb_field {
+ u8 len; /* Field length */
+ u16 pos; /* Offset on the raw packet */
+ u8 col; /* Column -used for shifting */
+};
+
+/*
+ * RF analog register definition
+ */
+struct ath5k_rf_reg {
+ u8 bank; /* RF Buffer Bank number */
+ u8 index; /* Register's index on rf_regs_idx */
+ struct ath5k_rfb_field field; /* RF Buffer field for this register */
+};
+
+/* Map RF registers to indexes
+ * We do this to handle common bits and make our
+ * life easier by using an index for each register
+ * instead of a full rfb_field */
+enum ath5k_rf_regs_idx {
+ /* BANK 2 */
+ AR5K_RF_TURBO = 0,
+ /* BANK 6 */
+ AR5K_RF_OB_2GHZ,
+ AR5K_RF_OB_5GHZ,
+ AR5K_RF_DB_2GHZ,
+ AR5K_RF_DB_5GHZ,
+ AR5K_RF_FIXED_BIAS_A,
+ AR5K_RF_FIXED_BIAS_B,
+ AR5K_RF_PWD_XPD,
+ AR5K_RF_XPD_SEL,
+ AR5K_RF_XPD_GAIN,
+ AR5K_RF_PD_GAIN_LO,
+ AR5K_RF_PD_GAIN_HI,
+ AR5K_RF_HIGH_VC_CP,
+ AR5K_RF_MID_VC_CP,
+ AR5K_RF_LOW_VC_CP,
+ AR5K_RF_PUSH_UP,
+ AR5K_RF_PAD2GND,
+ AR5K_RF_XB2_LVL,
+ AR5K_RF_XB5_LVL,
+ AR5K_RF_PWD_ICLOBUF_2G,
+ AR5K_RF_PWD_84,
+ AR5K_RF_PWD_90,
+ AR5K_RF_PWD_130,
+ AR5K_RF_PWD_131,
+ AR5K_RF_PWD_132,
+ AR5K_RF_PWD_136,
+ AR5K_RF_PWD_137,
+ AR5K_RF_PWD_138,
+ AR5K_RF_PWD_166,
+ AR5K_RF_PWD_167,
+ AR5K_RF_DERBY_CHAN_SEL_MODE,
+ /* BANK 7 */
+ AR5K_RF_GAIN_I,
+ AR5K_RF_PLO_SEL,
+ AR5K_RF_RFGAIN_SEL,
+ AR5K_RF_RFGAIN_STEP,
+ AR5K_RF_WAIT_S,
+ AR5K_RF_WAIT_I,
+ AR5K_RF_MAX_TIME,
+ AR5K_RF_MIXVGA_OVR,
+ AR5K_RF_MIXGAIN_OVR,
+ AR5K_RF_MIXGAIN_STEP,
+ AR5K_RF_PD_DELAY_A,
+ AR5K_RF_PD_DELAY_B,
+ AR5K_RF_PD_DELAY_XR,
+ AR5K_RF_PD_PERIOD_A,
+ AR5K_RF_PD_PERIOD_B,
+ AR5K_RF_PD_PERIOD_XR,
+};
+
+
+/*******************\
+* RF5111 (Sombrero) *
+\*******************/
+
+/* BANK 2 len pos col */
+#define AR5K_RF5111_RF_TURBO { 1, 3, 0 }
+
+/* BANK 6 len pos col */
+#define AR5K_RF5111_OB_2GHZ { 3, 119, 0 }
+#define AR5K_RF5111_DB_2GHZ { 3, 122, 0 }
+
+#define AR5K_RF5111_OB_5GHZ { 3, 104, 0 }
+#define AR5K_RF5111_DB_5GHZ { 3, 107, 0 }
+
+#define AR5K_RF5111_PWD_XPD { 1, 95, 0 }
+#define AR5K_RF5111_XPD_GAIN { 4, 96, 0 }
+
+/* Access to PWD registers */
+#define AR5K_RF5111_PWD(_n) { 1, (135 - _n), 3 }
+
+/* BANK 7 len pos col */
+#define AR5K_RF5111_GAIN_I { 6, 29, 0 }
+#define AR5K_RF5111_PLO_SEL { 1, 4, 0 }
+#define AR5K_RF5111_RFGAIN_SEL { 1, 36, 0 }
+#define AR5K_RF5111_RFGAIN_STEP { 6, 37, 0 }
+/* Only on AR5212 BaseBand and up */
+#define AR5K_RF5111_WAIT_S { 5, 19, 0 }
+#define AR5K_RF5111_WAIT_I { 5, 24, 0 }
+#define AR5K_RF5111_MAX_TIME { 2, 49, 0 }
+
+static const struct ath5k_rf_reg rf_regs_5111[] = {
+ {2, AR5K_RF_TURBO, AR5K_RF5111_RF_TURBO},
+ {6, AR5K_RF_OB_2GHZ, AR5K_RF5111_OB_2GHZ},
+ {6, AR5K_RF_DB_2GHZ, AR5K_RF5111_DB_2GHZ},
+ {6, AR5K_RF_OB_5GHZ, AR5K_RF5111_OB_5GHZ},
+ {6, AR5K_RF_DB_5GHZ, AR5K_RF5111_DB_5GHZ},
+ {6, AR5K_RF_PWD_XPD, AR5K_RF5111_PWD_XPD},
+ {6, AR5K_RF_XPD_GAIN, AR5K_RF5111_XPD_GAIN},
+ {6, AR5K_RF_PWD_84, AR5K_RF5111_PWD(84)},
+ {6, AR5K_RF_PWD_90, AR5K_RF5111_PWD(90)},
+ {7, AR5K_RF_GAIN_I, AR5K_RF5111_GAIN_I},
+ {7, AR5K_RF_PLO_SEL, AR5K_RF5111_PLO_SEL},
+ {7, AR5K_RF_RFGAIN_SEL, AR5K_RF5111_RFGAIN_SEL},
+ {7, AR5K_RF_RFGAIN_STEP, AR5K_RF5111_RFGAIN_STEP},
+ {7, AR5K_RF_WAIT_S, AR5K_RF5111_WAIT_S},
+ {7, AR5K_RF_WAIT_I, AR5K_RF5111_WAIT_I},
+ {7, AR5K_RF_MAX_TIME, AR5K_RF5111_MAX_TIME}
+};
+
+/* Default mode specific settings */
+static const struct ath5k_ini_rfbuffer rfb_5111[] = {
+ /* BANK / C.R. A/XR B G */
+ { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 0, 0x989c, { 0x00380000, 0x00380000, 0x00380000 } },
+ { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 0, 0x989c, { 0x00000000, 0x000000c0, 0x00000080 } },
+ { 0, 0x989c, { 0x000400f9, 0x000400ff, 0x000400fd } },
+ { 0, 0x98d4, { 0x00000000, 0x00000004, 0x00000004 } },
+ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
+ { 2, 0x98d4, { 0x00000010, 0x00000010, 0x00000010 } },
+ { 3, 0x98d8, { 0x00601068, 0x00601068, 0x00601068 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x10000000, 0x10000000, 0x10000000 } },
+ { 6, 0x989c, { 0x04000000, 0x04000000, 0x04000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x0a000000, 0x00000000 } },
+ { 6, 0x989c, { 0x003800c0, 0x023800c0, 0x003800c0 } },
+ { 6, 0x989c, { 0x00020006, 0x00000006, 0x00020006 } },
+ { 6, 0x989c, { 0x00000089, 0x00000089, 0x00000089 } },
+ { 6, 0x989c, { 0x000000a0, 0x000000a0, 0x000000a0 } },
+ { 6, 0x989c, { 0x00040007, 0x00040007, 0x00040007 } },
+ { 6, 0x98d4, { 0x0000001a, 0x0000001a, 0x0000001a } },
+ { 7, 0x989c, { 0x00000040, 0x00000040, 0x00000040 } },
+ { 7, 0x989c, { 0x00000010, 0x00000010, 0x00000010 } },
+ { 7, 0x989c, { 0x00000008, 0x00000008, 0x00000008 } },
+ { 7, 0x989c, { 0x0000004f, 0x0000004f, 0x0000004f } },
+ { 7, 0x989c, { 0x000000f1, 0x00000061, 0x000000f1 } },
+ { 7, 0x989c, { 0x0000904f, 0x0000904c, 0x0000904f } },
+ { 7, 0x989c, { 0x0000125a, 0x0000129a, 0x0000125a } },
+ { 7, 0x98cc, { 0x0000000e, 0x0000000f, 0x0000000e } },
+};
+
+
+
+/***********************\
+* RF5112/RF2112 (Derby) *
+\***********************/
+
+/* BANK 2 (Common) len pos col */
+#define AR5K_RF5112X_RF_TURBO { 1, 1, 2 }
+
+/* BANK 7 (Common) len pos col */
+#define AR5K_RF5112X_GAIN_I { 6, 14, 0 }
+#define AR5K_RF5112X_MIXVGA_OVR { 1, 36, 0 }
+#define AR5K_RF5112X_MIXGAIN_OVR { 2, 37, 0 }
+#define AR5K_RF5112X_MIXGAIN_STEP { 4, 32, 0 }
+#define AR5K_RF5112X_PD_DELAY_A { 4, 58, 0 }
+#define AR5K_RF5112X_PD_DELAY_B { 4, 62, 0 }
+#define AR5K_RF5112X_PD_DELAY_XR { 4, 66, 0 }
+#define AR5K_RF5112X_PD_PERIOD_A { 4, 70, 0 }
+#define AR5K_RF5112X_PD_PERIOD_B { 4, 74, 0 }
+#define AR5K_RF5112X_PD_PERIOD_XR { 4, 78, 0 }
+
+/* RFX112 (Derby 1) */
+
+/* BANK 6 len pos col */
+#define AR5K_RF5112_OB_2GHZ { 3, 269, 0 }
+#define AR5K_RF5112_DB_2GHZ { 3, 272, 0 }
+
+#define AR5K_RF5112_OB_5GHZ { 3, 261, 0 }
+#define AR5K_RF5112_DB_5GHZ { 3, 264, 0 }
+
+#define AR5K_RF5112_FIXED_BIAS_A { 1, 260, 0 }
+#define AR5K_RF5112_FIXED_BIAS_B { 1, 259, 0 }
+
+#define AR5K_RF5112_XPD_SEL { 1, 284, 0 }
+#define AR5K_RF5112_XPD_GAIN { 2, 252, 0 }
+
+/* Access to PWD registers */
+#define AR5K_RF5112_PWD(_n) { 1, (302 - _n), 3 }
+
+static const struct ath5k_rf_reg rf_regs_5112[] = {
+ {2, AR5K_RF_TURBO, AR5K_RF5112X_RF_TURBO},
+ {6, AR5K_RF_OB_2GHZ, AR5K_RF5112_OB_2GHZ},
+ {6, AR5K_RF_DB_2GHZ, AR5K_RF5112_DB_2GHZ},
+ {6, AR5K_RF_OB_5GHZ, AR5K_RF5112_OB_5GHZ},
+ {6, AR5K_RF_DB_5GHZ, AR5K_RF5112_DB_5GHZ},
+ {6, AR5K_RF_FIXED_BIAS_A, AR5K_RF5112_FIXED_BIAS_A},
+ {6, AR5K_RF_FIXED_BIAS_B, AR5K_RF5112_FIXED_BIAS_B},
+ {6, AR5K_RF_XPD_SEL, AR5K_RF5112_XPD_SEL},
+ {6, AR5K_RF_XPD_GAIN, AR5K_RF5112_XPD_GAIN},
+ {6, AR5K_RF_PWD_130, AR5K_RF5112_PWD(130)},
+ {6, AR5K_RF_PWD_131, AR5K_RF5112_PWD(131)},
+ {6, AR5K_RF_PWD_132, AR5K_RF5112_PWD(132)},
+ {6, AR5K_RF_PWD_136, AR5K_RF5112_PWD(136)},
+ {6, AR5K_RF_PWD_137, AR5K_RF5112_PWD(137)},
+ {6, AR5K_RF_PWD_138, AR5K_RF5112_PWD(138)},
+ {7, AR5K_RF_GAIN_I, AR5K_RF5112X_GAIN_I},
+ {7, AR5K_RF_MIXVGA_OVR, AR5K_RF5112X_MIXVGA_OVR},
+ {7, AR5K_RF_MIXGAIN_OVR, AR5K_RF5112X_MIXGAIN_OVR},
+ {7, AR5K_RF_MIXGAIN_STEP, AR5K_RF5112X_MIXGAIN_STEP},
+ {7, AR5K_RF_PD_DELAY_A, AR5K_RF5112X_PD_DELAY_A},
+ {7, AR5K_RF_PD_DELAY_B, AR5K_RF5112X_PD_DELAY_B},
+ {7, AR5K_RF_PD_DELAY_XR, AR5K_RF5112X_PD_DELAY_XR},
+ {7, AR5K_RF_PD_PERIOD_A, AR5K_RF5112X_PD_PERIOD_A},
+ {7, AR5K_RF_PD_PERIOD_B, AR5K_RF5112X_PD_PERIOD_B},
+ {7, AR5K_RF_PD_PERIOD_XR, AR5K_RF5112X_PD_PERIOD_XR},
+};
+
+/* Default mode specific settings */
+static const struct ath5k_ini_rfbuffer rfb_5112[] = {
+ /* BANK / C.R. A/XR B G */
+ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
+ { 2, 0x98d0, { 0x03060408, 0x03060408, 0x03060408 } },
+ { 3, 0x98dc, { 0x00a0c0c0, 0x00e0c0c0, 0x00e0c0c0 } },
+ { 6, 0x989c, { 0x00a00000, 0x00a00000, 0x00a00000 } },
+ { 6, 0x989c, { 0x000a0000, 0x000a0000, 0x000a0000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00660000, 0x00660000, 0x00660000 } },
+ { 6, 0x989c, { 0x00db0000, 0x00db0000, 0x00db0000 } },
+ { 6, 0x989c, { 0x00f10000, 0x00f10000, 0x00f10000 } },
+ { 6, 0x989c, { 0x00120000, 0x00120000, 0x00120000 } },
+ { 6, 0x989c, { 0x00120000, 0x00120000, 0x00120000 } },
+ { 6, 0x989c, { 0x00730000, 0x00730000, 0x00730000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x000c0000, 0x000c0000, 0x000c0000 } },
+ { 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+ { 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+ { 6, 0x989c, { 0x008b0000, 0x008b0000, 0x008b0000 } },
+ { 6, 0x989c, { 0x00600000, 0x00600000, 0x00600000 } },
+ { 6, 0x989c, { 0x000c0000, 0x000c0000, 0x000c0000 } },
+ { 6, 0x989c, { 0x00840000, 0x00840000, 0x00840000 } },
+ { 6, 0x989c, { 0x00640000, 0x00640000, 0x00640000 } },
+ { 6, 0x989c, { 0x00200000, 0x00200000, 0x00200000 } },
+ { 6, 0x989c, { 0x00240000, 0x00240000, 0x00240000 } },
+ { 6, 0x989c, { 0x00250000, 0x00250000, 0x00250000 } },
+ { 6, 0x989c, { 0x00110000, 0x00110000, 0x00110000 } },
+ { 6, 0x989c, { 0x00110000, 0x00110000, 0x00110000 } },
+ { 6, 0x989c, { 0x00510000, 0x00510000, 0x00510000 } },
+ { 6, 0x989c, { 0x1c040000, 0x1c040000, 0x1c040000 } },
+ { 6, 0x989c, { 0x000a0000, 0x000a0000, 0x000a0000 } },
+ { 6, 0x989c, { 0x00a10000, 0x00a10000, 0x00a10000 } },
+ { 6, 0x989c, { 0x00400000, 0x00400000, 0x00400000 } },
+ { 6, 0x989c, { 0x03090000, 0x03090000, 0x03090000 } },
+ { 6, 0x989c, { 0x06000000, 0x06000000, 0x06000000 } },
+ { 6, 0x989c, { 0x000000b0, 0x000000a8, 0x000000a8 } },
+ { 6, 0x989c, { 0x0000002e, 0x0000002e, 0x0000002e } },
+ { 6, 0x989c, { 0x006c4a41, 0x006c4af1, 0x006c4a61 } },
+ { 6, 0x989c, { 0x0050892a, 0x0050892b, 0x0050892b } },
+ { 6, 0x989c, { 0x00842400, 0x00842400, 0x00842400 } },
+ { 6, 0x989c, { 0x00c69200, 0x00c69200, 0x00c69200 } },
+ { 6, 0x98d0, { 0x0002000c, 0x0002000c, 0x0002000c } },
+ { 7, 0x989c, { 0x00000094, 0x00000094, 0x00000094 } },
+ { 7, 0x989c, { 0x00000091, 0x00000091, 0x00000091 } },
+ { 7, 0x989c, { 0x0000000a, 0x00000012, 0x00000012 } },
+ { 7, 0x989c, { 0x00000080, 0x00000080, 0x00000080 } },
+ { 7, 0x989c, { 0x000000c1, 0x000000c1, 0x000000c1 } },
+ { 7, 0x989c, { 0x00000060, 0x00000060, 0x00000060 } },
+ { 7, 0x989c, { 0x000000f0, 0x000000f0, 0x000000f0 } },
+ { 7, 0x989c, { 0x00000022, 0x00000022, 0x00000022 } },
+ { 7, 0x989c, { 0x00000092, 0x00000092, 0x00000092 } },
+ { 7, 0x989c, { 0x000000d4, 0x000000d4, 0x000000d4 } },
+ { 7, 0x989c, { 0x000014cc, 0x000014cc, 0x000014cc } },
+ { 7, 0x989c, { 0x0000048c, 0x0000048c, 0x0000048c } },
+ { 7, 0x98c4, { 0x00000003, 0x00000003, 0x00000003 } },
+};
+
+/* RFX112A (Derby 2) */
+
+/* BANK 6 len pos col */
+#define AR5K_RF5112A_OB_2GHZ { 3, 287, 0 }
+#define AR5K_RF5112A_DB_2GHZ { 3, 290, 0 }
+
+#define AR5K_RF5112A_OB_5GHZ { 3, 279, 0 }
+#define AR5K_RF5112A_DB_5GHZ { 3, 282, 0 }
+
+#define AR5K_RF5112A_FIXED_BIAS_A { 1, 278, 0 }
+#define AR5K_RF5112A_FIXED_BIAS_B { 1, 277, 0 }
+
+#define AR5K_RF5112A_XPD_SEL { 1, 302, 0 }
+#define AR5K_RF5112A_PDGAINLO { 2, 270, 0 }
+#define AR5K_RF5112A_PDGAINHI { 2, 257, 0 }
+
+/* Access to PWD registers */
+#define AR5K_RF5112A_PWD(_n) { 1, (306 - _n), 3 }
+
+/* Voltage regulators */
+#define AR5K_RF5112A_HIGH_VC_CP { 2, 90, 2 }
+#define AR5K_RF5112A_MID_VC_CP { 2, 92, 2 }
+#define AR5K_RF5112A_LOW_VC_CP { 2, 94, 2 }
+#define AR5K_RF5112A_PUSH_UP { 1, 254, 2 }
+
+/* Power consumption */
+#define AR5K_RF5112A_PAD2GND { 1, 281, 1 }
+#define AR5K_RF5112A_XB2_LVL { 2, 1, 3 }
+#define AR5K_RF5112A_XB5_LVL { 2, 3, 3 }
+
+static const struct ath5k_rf_reg rf_regs_5112a[] = {
+ {2, AR5K_RF_TURBO, AR5K_RF5112X_RF_TURBO},
+ {6, AR5K_RF_OB_2GHZ, AR5K_RF5112A_OB_2GHZ},
+ {6, AR5K_RF_DB_2GHZ, AR5K_RF5112A_DB_2GHZ},
+ {6, AR5K_RF_OB_5GHZ, AR5K_RF5112A_OB_5GHZ},
+ {6, AR5K_RF_DB_5GHZ, AR5K_RF5112A_DB_5GHZ},
+ {6, AR5K_RF_FIXED_BIAS_A, AR5K_RF5112A_FIXED_BIAS_A},
+ {6, AR5K_RF_FIXED_BIAS_B, AR5K_RF5112A_FIXED_BIAS_B},
+ {6, AR5K_RF_XPD_SEL, AR5K_RF5112A_XPD_SEL},
+ {6, AR5K_RF_PD_GAIN_LO, AR5K_RF5112A_PDGAINLO},
+ {6, AR5K_RF_PD_GAIN_HI, AR5K_RF5112A_PDGAINHI},
+ {6, AR5K_RF_PWD_130, AR5K_RF5112A_PWD(130)},
+ {6, AR5K_RF_PWD_131, AR5K_RF5112A_PWD(131)},
+ {6, AR5K_RF_PWD_132, AR5K_RF5112A_PWD(132)},
+ {6, AR5K_RF_PWD_136, AR5K_RF5112A_PWD(136)},
+ {6, AR5K_RF_PWD_137, AR5K_RF5112A_PWD(137)},
+ {6, AR5K_RF_PWD_138, AR5K_RF5112A_PWD(138)},
+ {6, AR5K_RF_PWD_166, AR5K_RF5112A_PWD(166)},
+ {6, AR5K_RF_PWD_167, AR5K_RF5112A_PWD(167)},
+ {6, AR5K_RF_HIGH_VC_CP, AR5K_RF5112A_HIGH_VC_CP},
+ {6, AR5K_RF_MID_VC_CP, AR5K_RF5112A_MID_VC_CP},
+ {6, AR5K_RF_LOW_VC_CP, AR5K_RF5112A_LOW_VC_CP},
+ {6, AR5K_RF_PUSH_UP, AR5K_RF5112A_PUSH_UP},
+ {6, AR5K_RF_PAD2GND, AR5K_RF5112A_PAD2GND},
+ {6, AR5K_RF_XB2_LVL, AR5K_RF5112A_XB2_LVL},
+ {6, AR5K_RF_XB5_LVL, AR5K_RF5112A_XB5_LVL},
+ {7, AR5K_RF_GAIN_I, AR5K_RF5112X_GAIN_I},
+ {7, AR5K_RF_MIXVGA_OVR, AR5K_RF5112X_MIXVGA_OVR},
+ {7, AR5K_RF_MIXGAIN_OVR, AR5K_RF5112X_MIXGAIN_OVR},
+ {7, AR5K_RF_MIXGAIN_STEP, AR5K_RF5112X_MIXGAIN_STEP},
+ {7, AR5K_RF_PD_DELAY_A, AR5K_RF5112X_PD_DELAY_A},
+ {7, AR5K_RF_PD_DELAY_B, AR5K_RF5112X_PD_DELAY_B},
+ {7, AR5K_RF_PD_DELAY_XR, AR5K_RF5112X_PD_DELAY_XR},
+ {7, AR5K_RF_PD_PERIOD_A, AR5K_RF5112X_PD_PERIOD_A},
+ {7, AR5K_RF_PD_PERIOD_B, AR5K_RF5112X_PD_PERIOD_B},
+ {7, AR5K_RF_PD_PERIOD_XR, AR5K_RF5112X_PD_PERIOD_XR},
+};
+
+/* Default mode specific settings */
+static const struct ath5k_ini_rfbuffer rfb_5112a[] = {
+ /* BANK / C.R. A/XR B G */
+ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
+ { 2, 0x98d0, { 0x03060408, 0x03060408, 0x03060408 } },
+ { 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } },
+ { 6, 0x989c, { 0x0f000000, 0x0f000000, 0x0f000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00800000, 0x00800000, 0x00800000 } },
+ { 6, 0x989c, { 0x002a0000, 0x002a0000, 0x002a0000 } },
+ { 6, 0x989c, { 0x00010000, 0x00010000, 0x00010000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00180000, 0x00180000, 0x00180000 } },
+ { 6, 0x989c, { 0x00600000, 0x006e0000, 0x006e0000 } },
+ { 6, 0x989c, { 0x00c70000, 0x00c70000, 0x00c70000 } },
+ { 6, 0x989c, { 0x004b0000, 0x004b0000, 0x004b0000 } },
+ { 6, 0x989c, { 0x04480000, 0x04480000, 0x04480000 } },
+ { 6, 0x989c, { 0x004c0000, 0x004c0000, 0x004c0000 } },
+ { 6, 0x989c, { 0x00e40000, 0x00e40000, 0x00e40000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00fc0000, 0x00fc0000, 0x00fc0000 } },
+ { 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+ { 6, 0x989c, { 0x043f0000, 0x043f0000, 0x043f0000 } },
+ { 6, 0x989c, { 0x000c0000, 0x000c0000, 0x000c0000 } },
+ { 6, 0x989c, { 0x02190000, 0x02190000, 0x02190000 } },
+ { 6, 0x989c, { 0x00240000, 0x00240000, 0x00240000 } },
+ { 6, 0x989c, { 0x00b40000, 0x00b40000, 0x00b40000 } },
+ { 6, 0x989c, { 0x00990000, 0x00990000, 0x00990000 } },
+ { 6, 0x989c, { 0x00500000, 0x00500000, 0x00500000 } },
+ { 6, 0x989c, { 0x002a0000, 0x002a0000, 0x002a0000 } },
+ { 6, 0x989c, { 0x00120000, 0x00120000, 0x00120000 } },
+ { 6, 0x989c, { 0xc0320000, 0xc0320000, 0xc0320000 } },
+ { 6, 0x989c, { 0x01740000, 0x01740000, 0x01740000 } },
+ { 6, 0x989c, { 0x00110000, 0x00110000, 0x00110000 } },
+ { 6, 0x989c, { 0x86280000, 0x86280000, 0x86280000 } },
+ { 6, 0x989c, { 0x31840000, 0x31840000, 0x31840000 } },
+ { 6, 0x989c, { 0x00f20080, 0x00f20080, 0x00f20080 } },
+ { 6, 0x989c, { 0x00270019, 0x00270019, 0x00270019 } },
+ { 6, 0x989c, { 0x00000003, 0x00000003, 0x00000003 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x000000b2, 0x000000b2, 0x000000b2 } },
+ { 6, 0x989c, { 0x00b02084, 0x00b02084, 0x00b02084 } },
+ { 6, 0x989c, { 0x004125a4, 0x004125a4, 0x004125a4 } },
+ { 6, 0x989c, { 0x00119220, 0x00119220, 0x00119220 } },
+ { 6, 0x989c, { 0x001a4800, 0x001a4800, 0x001a4800 } },
+ { 6, 0x98d8, { 0x000b0230, 0x000b0230, 0x000b0230 } },
+ { 7, 0x989c, { 0x00000094, 0x00000094, 0x00000094 } },
+ { 7, 0x989c, { 0x00000091, 0x00000091, 0x00000091 } },
+ { 7, 0x989c, { 0x00000012, 0x00000012, 0x00000012 } },
+ { 7, 0x989c, { 0x00000080, 0x00000080, 0x00000080 } },
+ { 7, 0x989c, { 0x000000d9, 0x000000d9, 0x000000d9 } },
+ { 7, 0x989c, { 0x00000060, 0x00000060, 0x00000060 } },
+ { 7, 0x989c, { 0x000000f0, 0x000000f0, 0x000000f0 } },
+ { 7, 0x989c, { 0x000000a2, 0x000000a2, 0x000000a2 } },
+ { 7, 0x989c, { 0x00000052, 0x00000052, 0x00000052 } },
+ { 7, 0x989c, { 0x000000d4, 0x000000d4, 0x000000d4 } },
+ { 7, 0x989c, { 0x000014cc, 0x000014cc, 0x000014cc } },
+ { 7, 0x989c, { 0x0000048c, 0x0000048c, 0x0000048c } },
+ { 7, 0x98c4, { 0x00000003, 0x00000003, 0x00000003 } },
+};
+
+
+
+/******************\
+* RF2413 (Griffin) *
+\******************/
+
+/* BANK 2 len pos col */
+#define AR5K_RF2413_RF_TURBO { 1, 1, 2 }
+
+/* BANK 6 len pos col */
+#define AR5K_RF2413_OB_2GHZ { 3, 168, 0 }
+#define AR5K_RF2413_DB_2GHZ { 3, 165, 0 }
+
+static const struct ath5k_rf_reg rf_regs_2413[] = {
+ {2, AR5K_RF_TURBO, AR5K_RF2413_RF_TURBO},
+ {6, AR5K_RF_OB_2GHZ, AR5K_RF2413_OB_2GHZ},
+ {6, AR5K_RF_DB_2GHZ, AR5K_RF2413_DB_2GHZ},
+};
+
+/* Default mode specific settings
+ * XXX: a/aTurbo ???
+ */
+static const struct ath5k_ini_rfbuffer rfb_2413[] = {
+ /* BANK / C.R. A/XR B G */
+ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
+ { 2, 0x98d0, { 0x02001408, 0x02001408, 0x02001408 } },
+ { 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } },
+ { 6, 0x989c, { 0xf0000000, 0xf0000000, 0xf0000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x03000000, 0x03000000, 0x03000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x40400000, 0x40400000, 0x40400000 } },
+ { 6, 0x989c, { 0x65050000, 0x65050000, 0x65050000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00420000, 0x00420000, 0x00420000 } },
+ { 6, 0x989c, { 0x00b50000, 0x00b50000, 0x00b50000 } },
+ { 6, 0x989c, { 0x00030000, 0x00030000, 0x00030000 } },
+ { 6, 0x989c, { 0x00f70000, 0x00f70000, 0x00f70000 } },
+ { 6, 0x989c, { 0x009d0000, 0x009d0000, 0x009d0000 } },
+ { 6, 0x989c, { 0x00220000, 0x00220000, 0x00220000 } },
+ { 6, 0x989c, { 0x04220000, 0x04220000, 0x04220000 } },
+ { 6, 0x989c, { 0x00230018, 0x00230018, 0x00230018 } },
+ { 6, 0x989c, { 0x00280000, 0x00280060, 0x00280060 } },
+ { 6, 0x989c, { 0x005000c0, 0x005000c3, 0x005000c3 } },
+ { 6, 0x989c, { 0x0004007f, 0x0004007f, 0x0004007f } },
+ { 6, 0x989c, { 0x00000458, 0x00000458, 0x00000458 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x0000c000, 0x0000c000, 0x0000c000 } },
+ { 6, 0x98d8, { 0x00400230, 0x00400230, 0x00400230 } },
+ { 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } },
+ { 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } },
+ { 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } },
+};
+
+
+
+/***************************\
+* RF2315/RF2316 (Cobra SoC) *
+\***************************/
+
+/* BANK 2 len pos col */
+#define AR5K_RF2316_RF_TURBO { 1, 1, 2 }
+
+/* BANK 6 len pos col */
+#define AR5K_RF2316_OB_2GHZ { 3, 178, 0 }
+#define AR5K_RF2316_DB_2GHZ { 3, 175, 0 }
+
+static const struct ath5k_rf_reg rf_regs_2316[] = {
+ {2, AR5K_RF_TURBO, AR5K_RF2316_RF_TURBO},
+ {6, AR5K_RF_OB_2GHZ, AR5K_RF2316_OB_2GHZ},
+ {6, AR5K_RF_DB_2GHZ, AR5K_RF2316_DB_2GHZ},
+};
+
+/* Default mode specific settings */
+static const struct ath5k_ini_rfbuffer rfb_2316[] = {
+ /* BANK / C.R. A/XR B G */
+ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
+ { 2, 0x98d0, { 0x02001408, 0x02001408, 0x02001408 } },
+ { 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0xc0000000, 0xc0000000, 0xc0000000 } },
+ { 6, 0x989c, { 0x0f000000, 0x0f000000, 0x0f000000 } },
+ { 6, 0x989c, { 0x02000000, 0x02000000, 0x02000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0xf8000000, 0xf8000000, 0xf8000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x95150000, 0x95150000, 0x95150000 } },
+ { 6, 0x989c, { 0xc1000000, 0xc1000000, 0xc1000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00080000, 0x00080000, 0x00080000 } },
+ { 6, 0x989c, { 0x00d50000, 0x00d50000, 0x00d50000 } },
+ { 6, 0x989c, { 0x000e0000, 0x000e0000, 0x000e0000 } },
+ { 6, 0x989c, { 0x00dc0000, 0x00dc0000, 0x00dc0000 } },
+ { 6, 0x989c, { 0x00770000, 0x00770000, 0x00770000 } },
+ { 6, 0x989c, { 0x008a0000, 0x008a0000, 0x008a0000 } },
+ { 6, 0x989c, { 0x10880000, 0x10880000, 0x10880000 } },
+ { 6, 0x989c, { 0x008c0060, 0x008c0060, 0x008c0060 } },
+ { 6, 0x989c, { 0x00a00000, 0x00a00080, 0x00a00080 } },
+ { 6, 0x989c, { 0x00400000, 0x0040000d, 0x0040000d } },
+ { 6, 0x989c, { 0x00110400, 0x00110400, 0x00110400 } },
+ { 6, 0x989c, { 0x00000060, 0x00000060, 0x00000060 } },
+ { 6, 0x989c, { 0x00000001, 0x00000001, 0x00000001 } },
+ { 6, 0x989c, { 0x00000b00, 0x00000b00, 0x00000b00 } },
+ { 6, 0x989c, { 0x00000be8, 0x00000be8, 0x00000be8 } },
+ { 6, 0x98c0, { 0x00010000, 0x00010000, 0x00010000 } },
+ { 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } },
+ { 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } },
+ { 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } },
+};
+
+
+
+/******************************\
+* RF5413/RF5424 (Eagle/Condor) *
+\******************************/
+
+/* BANK 6 len pos col */
+#define AR5K_RF5413_OB_2GHZ { 3, 241, 0 }
+#define AR5K_RF5413_DB_2GHZ { 3, 238, 0 }
+
+#define AR5K_RF5413_OB_5GHZ { 3, 247, 0 }
+#define AR5K_RF5413_DB_5GHZ { 3, 244, 0 }
+
+#define AR5K_RF5413_PWD_ICLOBUF2G { 3, 131, 3 }
+#define AR5K_RF5413_DERBY_CHAN_SEL_MODE { 1, 291, 2 }
+
+static const struct ath5k_rf_reg rf_regs_5413[] = {
+ {6, AR5K_RF_OB_2GHZ, AR5K_RF5413_OB_2GHZ},
+ {6, AR5K_RF_DB_2GHZ, AR5K_RF5413_DB_2GHZ},
+ {6, AR5K_RF_OB_5GHZ, AR5K_RF5413_OB_5GHZ},
+ {6, AR5K_RF_DB_5GHZ, AR5K_RF5413_DB_5GHZ},
+ {6, AR5K_RF_PWD_ICLOBUF_2G, AR5K_RF5413_PWD_ICLOBUF2G},
+ {6, AR5K_RF_DERBY_CHAN_SEL_MODE, AR5K_RF5413_DERBY_CHAN_SEL_MODE},
+};
+
+/* Default mode specific settings */
+static const struct ath5k_ini_rfbuffer rfb_5413[] = {
+ /* BANK / C.R. A/XR B G */
+ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
+ { 2, 0x98d0, { 0x00000008, 0x00000008, 0x00000008 } },
+ { 3, 0x98dc, { 0x00a000c0, 0x00e000c0, 0x00e000c0 } },
+ { 6, 0x989c, { 0x33000000, 0x33000000, 0x33000000 } },
+ { 6, 0x989c, { 0x01000000, 0x01000000, 0x01000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x1f000000, 0x1f000000, 0x1f000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00b80000, 0x00b80000, 0x00b80000 } },
+ { 6, 0x989c, { 0x00b70000, 0x00b70000, 0x00b70000 } },
+ { 6, 0x989c, { 0x00840000, 0x00840000, 0x00840000 } },
+ { 6, 0x989c, { 0x00980000, 0x00980000, 0x00980000 } },
+ { 6, 0x989c, { 0x00c00000, 0x00c00000, 0x00c00000 } },
+ { 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+ { 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+ { 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+ { 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+ { 6, 0x989c, { 0x00d70000, 0x00d70000, 0x00d70000 } },
+ { 6, 0x989c, { 0x00610000, 0x00610000, 0x00610000 } },
+ { 6, 0x989c, { 0x00fe0000, 0x00fe0000, 0x00fe0000 } },
+ { 6, 0x989c, { 0x00de0000, 0x00de0000, 0x00de0000 } },
+ { 6, 0x989c, { 0x007f0000, 0x007f0000, 0x007f0000 } },
+ { 6, 0x989c, { 0x043d0000, 0x043d0000, 0x043d0000 } },
+ { 6, 0x989c, { 0x00770000, 0x00770000, 0x00770000 } },
+ { 6, 0x989c, { 0x00440000, 0x00440000, 0x00440000 } },
+ { 6, 0x989c, { 0x00980000, 0x00980000, 0x00980000 } },
+ { 6, 0x989c, { 0x00100080, 0x00100080, 0x00100080 } },
+ { 6, 0x989c, { 0x0005c034, 0x0005c034, 0x0005c034 } },
+ { 6, 0x989c, { 0x003100f0, 0x003100f0, 0x003100f0 } },
+ { 6, 0x989c, { 0x000c011f, 0x000c011f, 0x000c011f } },
+ { 6, 0x989c, { 0x00510040, 0x00510040, 0x00510040 } },
+ { 6, 0x989c, { 0x005000da, 0x005000da, 0x005000da } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00004044, 0x00004044, 0x00004044 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x000060c0, 0x000060c0, 0x000060c0 } },
+ { 6, 0x989c, { 0x00002c00, 0x00003600, 0x00003600 } },
+ { 6, 0x98c8, { 0x00000403, 0x00040403, 0x00040403 } },
+ { 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } },
+ { 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } },
+ { 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } },
+};
+
+
+
+/***************************\
+* RF2425/RF2417 (Swan/Nala) *
+* AR2317 (Spider SoC) *
+\***************************/
+
+/* BANK 2 len pos col */
+#define AR5K_RF2425_RF_TURBO { 1, 1, 2 }
+
+/* BANK 6 len pos col */
+#define AR5K_RF2425_OB_2GHZ { 3, 193, 0 }
+#define AR5K_RF2425_DB_2GHZ { 3, 190, 0 }
+
+static const struct ath5k_rf_reg rf_regs_2425[] = {
+ {2, AR5K_RF_TURBO, AR5K_RF2425_RF_TURBO},
+ {6, AR5K_RF_OB_2GHZ, AR5K_RF2425_OB_2GHZ},
+ {6, AR5K_RF_DB_2GHZ, AR5K_RF2425_DB_2GHZ},
+};
+
+/* Default mode specific settings
+ */
+static const struct ath5k_ini_rfbuffer rfb_2425[] = {
+ /* BANK / C.R. A/XR B G */
+ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
+ { 2, 0x98d0, { 0x02001408, 0x02001408, 0x02001408 } },
+ { 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } },
+ { 6, 0x989c, { 0x10000000, 0x10000000, 0x10000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x002a0000, 0x002a0000, 0x002a0000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00100000, 0x00100000, 0x00100000 } },
+ { 6, 0x989c, { 0x00020000, 0x00020000, 0x00020000 } },
+ { 6, 0x989c, { 0x00730000, 0x00730000, 0x00730000 } },
+ { 6, 0x989c, { 0x00f80000, 0x00f80000, 0x00f80000 } },
+ { 6, 0x989c, { 0x00e70000, 0x00e70000, 0x00e70000 } },
+ { 6, 0x989c, { 0x00140000, 0x00140000, 0x00140000 } },
+ { 6, 0x989c, { 0x00910040, 0x00910040, 0x00910040 } },
+ { 6, 0x989c, { 0x0007001a, 0x0007001a, 0x0007001a } },
+ { 6, 0x989c, { 0x00410000, 0x00410000, 0x00410000 } },
+ { 6, 0x989c, { 0x00810000, 0x00810060, 0x00810060 } },
+ { 6, 0x989c, { 0x00020800, 0x00020803, 0x00020803 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00001660, 0x00001660, 0x00001660 } },
+ { 6, 0x989c, { 0x00001688, 0x00001688, 0x00001688 } },
+ { 6, 0x98c4, { 0x00000001, 0x00000001, 0x00000001 } },
+ { 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } },
+ { 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } },
+ { 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } },
+};
+
+/*
+ * TODO: Handle the few differences with swan during
+ * bank modification and get rid of this
+ */
+static const struct ath5k_ini_rfbuffer rfb_2317[] = {
+ /* BANK / C.R. A/XR B G */
+ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
+ { 2, 0x98d0, { 0x02001408, 0x02001408, 0x02001408 } },
+ { 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } },
+ { 6, 0x989c, { 0x10000000, 0x10000000, 0x10000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x002a0000, 0x002a0000, 0x002a0000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00100000, 0x00100000, 0x00100000 } },
+ { 6, 0x989c, { 0x00020000, 0x00020000, 0x00020000 } },
+ { 6, 0x989c, { 0x00730000, 0x00730000, 0x00730000 } },
+ { 6, 0x989c, { 0x00f80000, 0x00f80000, 0x00f80000 } },
+ { 6, 0x989c, { 0x00e70000, 0x00e70000, 0x00e70000 } },
+ { 6, 0x989c, { 0x00140100, 0x00140100, 0x00140100 } },
+ { 6, 0x989c, { 0x00910040, 0x00910040, 0x00910040 } },
+ { 6, 0x989c, { 0x0007001a, 0x0007001a, 0x0007001a } },
+ { 6, 0x989c, { 0x00410000, 0x00410000, 0x00410000 } },
+ { 6, 0x989c, { 0x00810000, 0x00810060, 0x00810060 } },
+ { 6, 0x989c, { 0x00020800, 0x00020803, 0x00020803 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00001660, 0x00001660, 0x00001660 } },
+ { 6, 0x989c, { 0x00009688, 0x00009688, 0x00009688 } },
+ { 6, 0x98c4, { 0x00000001, 0x00000001, 0x00000001 } },
+ { 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } },
+ { 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } },
+ { 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } },
+};
+
+/*
+ * TODO: Handle the few differences with swan during
+ * bank modification and get rid of this
+ */
+static const struct ath5k_ini_rfbuffer rfb_2417[] = {
+ /* BANK / C.R. A/XR B G */
+ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
+ { 2, 0x98d0, { 0x02001408, 0x02001408, 0x02001408 } },
+ { 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } },
+ { 6, 0x989c, { 0x10000000, 0x10000000, 0x10000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x002a0000, 0x002a0000, 0x002a0000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00100000, 0x00100000, 0x00100000 } },
+ { 6, 0x989c, { 0x00020000, 0x00020000, 0x00020000 } },
+ { 6, 0x989c, { 0x00730000, 0x00730000, 0x00730000 } },
+ { 6, 0x989c, { 0x00f80000, 0x00f80000, 0x00f80000 } },
+ { 6, 0x989c, { 0x00e70000, 0x80e70000, 0x80e70000 } },
+ { 6, 0x989c, { 0x00140000, 0x00140000, 0x00140000 } },
+ { 6, 0x989c, { 0x00910040, 0x00910040, 0x00910040 } },
+ { 6, 0x989c, { 0x0007001a, 0x0207001a, 0x0207001a } },
+ { 6, 0x989c, { 0x00410000, 0x00410000, 0x00410000 } },
+ { 6, 0x989c, { 0x00810000, 0x00810060, 0x00810060 } },
+ { 6, 0x989c, { 0x00020800, 0x00020803, 0x00020803 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
+ { 6, 0x989c, { 0x00001660, 0x00001660, 0x00001660 } },
+ { 6, 0x989c, { 0x00001688, 0x00001688, 0x00001688 } },
+ { 6, 0x98c4, { 0x00000001, 0x00000001, 0x00000001 } },
+ { 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } },
+ { 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } },
+ { 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } },
+};
diff --git a/drivers/net/wireless/ath/ath5k/rfgain.h b/drivers/net/wireless/ath/ath5k/rfgain.h
new file mode 100644
index 0000000..ebfae05
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/rfgain.h
@@ -0,0 +1,516 @@
+/*
+ * RF Gain optimization
+ *
+ * Copyright (c) 2004-2009 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com>
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ */
+
+/*
+ * Mode-specific RF Gain table (64bytes) for RF5111/5112
+ * (RF5110 only comes with AR5210 and only supports a/turbo a mode so initial
+ * RF Gain values are included in AR5K_AR5210_INI)
+ */
+struct ath5k_ini_rfgain {
+ u16 rfg_register; /* RF Gain register address */
+ u32 rfg_value[2]; /* [freq (see below)] */
+};
+
+/* Initial RF Gain settings for RF5111 */
+static const struct ath5k_ini_rfgain rfgain_5111[] = {
+ /* 5GHz 2GHz */
+ { AR5K_RF_GAIN(0), { 0x000001a9, 0x00000000 } },
+ { AR5K_RF_GAIN(1), { 0x000001e9, 0x00000040 } },
+ { AR5K_RF_GAIN(2), { 0x00000029, 0x00000080 } },
+ { AR5K_RF_GAIN(3), { 0x00000069, 0x00000150 } },
+ { AR5K_RF_GAIN(4), { 0x00000199, 0x00000190 } },
+ { AR5K_RF_GAIN(5), { 0x000001d9, 0x000001d0 } },
+ { AR5K_RF_GAIN(6), { 0x00000019, 0x00000010 } },