ath9k_hw: start building an abstraction layer for hardware routines
authorLuis R. Rodriguez <lrodriguez@atheros.com>
Thu, 15 Apr 2010 21:38:06 +0000 (17:38 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 16 Apr 2010 19:32:01 +0000 (15:32 -0400)
ath9k supports the AR5008, AR9001 and AR9002 family of Atheros
chipsets, all 802.11n. The new breed of 802.11n chips, the
AR9003 family will be supported as well soon. To help with its
support we're going to add a few callbacks for hardware routines
which differ considerably instead of adding branch checks for
the revision at runtime.

Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/common.h
drivers/net/wireless/ath/ath9k/hw-ops.h [new file with mode: 0644]
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c

index 72a835d..1a87283 100644 (file)
@@ -20,6 +20,7 @@
 #include "../debug.h"
 
 #include "hw.h"
+#include "hw-ops.h"
 
 /* Common header for Atheros 802.11n base driver cores */
 
diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h
new file mode 100644 (file)
index 0000000..ee33146
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2010 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 ATH9K_HW_OPS_H
+#define ATH9K_HW_OPS_H
+
+#include "hw.h"
+
+/* Hardware core and driver accessible callbacks */
+
+static inline void ath9k_hw_configpcipowersave(struct ath_hw *ah,
+                                              int restore,
+                                              int power_off)
+{
+       ath9k_hw_ops(ah)->config_pci_powersave(ah, restore, power_off);
+}
+
+#endif /* ATH9K_HW_OPS_H */
index 3b9f4c1..a55db3b 100644 (file)
@@ -18,6 +18,7 @@
 #include <asm/unaligned.h>
 
 #include "hw.h"
+#include "hw-ops.h"
 #include "rc.h"
 #include "initvals.h"
 
@@ -25,6 +26,8 @@
 #define ATH9K_CLOCK_RATE_5GHZ_OFDM     40
 #define ATH9K_CLOCK_RATE_2GHZ_OFDM     44
 
+static void ar9002_hw_attach_ops(struct ath_hw *ah);
+
 static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type);
 static void ath9k_hw_set_regs(struct ath_hw *ah, struct ath9k_channel *chan);
 
@@ -45,6 +48,25 @@ static void __exit ath9k_exit(void)
 }
 module_exit(ath9k_exit);
 
+/* Private hardware callbacks */
+
+static void ath9k_hw_init_cal_settings(struct ath_hw *ah)
+{
+       ath9k_hw_private_ops(ah)->init_cal_settings(ah);
+}
+
+static void ath9k_hw_init_mode_regs(struct ath_hw *ah)
+{
+       ath9k_hw_private_ops(ah)->init_mode_regs(ah);
+}
+
+static bool ath9k_hw_macversion_supported(struct ath_hw *ah)
+{
+       struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
+
+       return priv_ops->macversion_supported(ah->hw_version.macVersion);
+}
+
 /********************/
 /* Helper Functions */
 /********************/
@@ -368,7 +390,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
        if (num_possible_cpus() > 1)
                ah->config.serialize_regmode = SER_REG_MODE_AUTO;
 }
-EXPORT_SYMBOL(ath9k_hw_init);
 
 static void ath9k_hw_init_defaults(struct ath_hw *ah)
 {
@@ -532,27 +553,7 @@ static int ath9k_hw_post_init(struct ath_hw *ah)
        return 0;
 }
 
-static bool ath9k_hw_devid_supported(u16 devid)
-{
-       switch (devid) {
-       case AR5416_DEVID_PCI:
-       case AR5416_DEVID_PCIE:
-       case AR5416_AR9100_DEVID:
-       case AR9160_DEVID_PCI:
-       case AR9280_DEVID_PCI:
-       case AR9280_DEVID_PCIE:
-       case AR9285_DEVID_PCIE:
-       case AR5416_DEVID_AR9287_PCI:
-       case AR5416_DEVID_AR9287_PCIE:
-       case AR2427_DEVID_PCIE:
-               return true;
-       default:
-               break;
-       }
-       return false;
-}
-
-static bool ath9k_hw_macversion_supported(u32 macversion)
+static bool ar9002_hw_macversion_supported(u32 macversion)
 {
        switch (macversion) {
        case AR_SREV_VERSION_5416_PCI:
@@ -570,7 +571,7 @@ static bool ath9k_hw_macversion_supported(u32 macversion)
        return false;
 }
 
-static void ath9k_hw_init_cal_settings(struct ath_hw *ah)
+static void ar9002_hw_init_cal_settings(struct ath_hw *ah)
 {
        if (AR_SREV_9160_10_OR_LATER(ah)) {
                if (AR_SREV_9280_10_OR_LATER(ah)) {
@@ -594,7 +595,7 @@ static void ath9k_hw_init_cal_settings(struct ath_hw *ah)
        }
 }
 
-static void ath9k_hw_init_mode_regs(struct ath_hw *ah)
+static void ar9002_hw_init_mode_regs(struct ath_hw *ah)
 {
        if (AR_SREV_9271(ah)) {
                INIT_INI_ARRAY(&ah->iniModes, ar9271Modes_9271,
@@ -854,20 +855,12 @@ static void ath9k_hw_init_eeprom_fix(struct ath_hw *ah)
                          "needs fixup for AR_AN_TOP2 register\n");
 }
 
-int ath9k_hw_init(struct ath_hw *ah)
+/* Called for all hardware families */
+static int __ath9k_hw_init(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
        int r = 0;
 
-       if (common->bus_ops->ath_bus_type != ATH_USB) {
-               if (!ath9k_hw_devid_supported(ah->hw_version.devid)) {
-                       ath_print(common, ATH_DBG_FATAL,
-                                 "Unsupported device ID: 0x%0x\n",
-                                 ah->hw_version.devid);
-                       return -EOPNOTSUPP;
-               }
-       }
-
        ath9k_hw_init_defaults(ah);
        ath9k_hw_init_config(ah);
 
@@ -877,6 +870,8 @@ int ath9k_hw_init(struct ath_hw *ah)
                return -EIO;
        }
 
+       ar9002_hw_attach_ops(ah);
+
        if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) {
                ath_print(common, ATH_DBG_FATAL, "Couldn't wakeup chip\n");
                return -EIO;
@@ -901,7 +896,7 @@ int ath9k_hw_init(struct ath_hw *ah)
        else
                ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD;
 
-       if (!ath9k_hw_macversion_supported(ah->hw_version.macVersion)) {
+       if (!ath9k_hw_macversion_supported(ah)) {
                ath_print(common, ATH_DBG_FATAL,
                          "Mac Chip Rev 0x%02x.%x is not supported by "
                          "this driver\n", ah->hw_version.macVersion,
@@ -918,6 +913,7 @@ int ath9k_hw_init(struct ath_hw *ah)
        if (AR_SREV_9271(ah))
                ah->is_pciexpress = false;
 
+       /* XXX: move this to its own hw op */
        ah->hw_version.phyRev = REG_READ(ah, AR_PHY_CHIP_ID);
 
        ath9k_hw_init_cal_settings(ah);
@@ -979,6 +975,45 @@ int ath9k_hw_init(struct ath_hw *ah)
        return 0;
 }
 
+int ath9k_hw_init(struct ath_hw *ah)
+{
+       int ret;
+       struct ath_common *common = ath9k_hw_common(ah);
+
+       /* These are all the AR5008/AR9001/AR9002 hardware family of chipsets */
+       switch (ah->hw_version.devid) {
+       case AR5416_DEVID_PCI:
+       case AR5416_DEVID_PCIE:
+       case AR5416_AR9100_DEVID:
+       case AR9160_DEVID_PCI:
+       case AR9280_DEVID_PCI:
+       case AR9280_DEVID_PCIE:
+       case AR9285_DEVID_PCIE:
+       case AR5416_DEVID_AR9287_PCI:
+       case AR5416_DEVID_AR9287_PCIE:
+       case AR2427_DEVID_PCIE:
+               break;
+       default:
+               if (common->bus_ops->ath_bus_type == ATH_USB)
+                       break;
+               ath_print(common, ATH_DBG_FATAL,
+                         "Hardware device ID 0x%04x not supported\n",
+                         ah->hw_version.devid);
+               return -EOPNOTSUPP;
+       }
+
+       ret = __ath9k_hw_init(ah);
+       if (ret) {
+               ath_print(common, ATH_DBG_FATAL,
+                         "Unable to initialize hardware; "
+                         "initialization status: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(ath9k_hw_init);
+
 static void ath9k_hw_init_bb(struct ath_hw *ah,
                             struct ath9k_channel *chan)
 {
@@ -2500,7 +2535,9 @@ EXPORT_SYMBOL(ath9k_hw_setpower);
  * Programming the SerDes must go through the same 288 bit serial shift
  * register as the other analog registers.  Hence the 9 writes.
  */
-void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore, int power_off)
+static void ar9002_hw_configpcipowersave(struct ath_hw *ah,
+                                        int restore,
+                                        int power_off)
 {
        u8 i;
        u32 val;
@@ -2518,7 +2555,7 @@ void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore, int power_off)
                        /*
                         * AR9280 2.0 or later chips use SerDes values from the
                         * initvals.h initialized depending on chipset during
-                        * ath9k_hw_init()
+                        * __ath9k_hw_init()
                         */
                        for (i = 0; i < ah->iniPcieSerdes.ia_rows; i++) {
                                REG_WRITE(ah, INI_RA(&ah->iniPcieSerdes, i, 0),
@@ -2622,7 +2659,6 @@ void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore, int power_off)
                }
        }
 }
-EXPORT_SYMBOL(ath9k_hw_configpcipowersave);
 
 /**********************/
 /* Interrupt Handling */
@@ -3917,3 +3953,16 @@ void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len)
        hw_name[used] = '\0';
 }
 EXPORT_SYMBOL(ath9k_hw_name);
+
+/* Sets up the AR5008/AR9001/AR9002 hardware familiy callbacks */
+static void ar9002_hw_attach_ops(struct ath_hw *ah)
+{
+       struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
+       struct ath_hw_ops *ops = ath9k_hw_ops(ah);
+
+       priv_ops->init_cal_settings = ar9002_hw_init_cal_settings;
+       priv_ops->init_mode_regs = ar9002_hw_init_mode_regs;
+       priv_ops->macversion_supported = ar9002_hw_macversion_supported;
+
+       ops->config_pci_powersave = ar9002_hw_configpcipowersave;
+}
index f4821cf..dfe8502 100644 (file)
@@ -440,6 +440,36 @@ struct ath_gen_timer_table {
        } timer_mask;
 };
 
+/**
+ * struct ath_hw_private_ops - callbacks used internally by hardware code
+ *
+ * This structure contains private callbacks designed to only be used internally
+ * by the hardware core.
+ *
+ * @init_cal_settings: Initializes calibration settings
+ * @init_mode_regs: Initializes mode registers
+ * @macversion_supported: If this specific mac revision is supported
+ */
+struct ath_hw_private_ops {
+       void (*init_cal_settings)(struct ath_hw *ah);
+       void (*init_mode_regs)(struct ath_hw *ah);
+       bool (*macversion_supported)(u32 macversion);
+};
+
+/**
+ * struct ath_hw_ops - callbacks used by hardware code and driver code
+ *
+ * This structure contains callbacks designed to to be used internally by
+ * hardware code and also by the lower level driver.
+ *
+ * @config_pci_powersave:
+ */
+struct ath_hw_ops {
+       void (*config_pci_powersave)(struct ath_hw *ah,
+                                    int restore,
+                                    int power_off);
+};
+
 struct ath_hw {
        struct ieee80211_hw *hw;
        struct ath_common common;
@@ -540,6 +570,11 @@ struct ath_hw {
        void (*ath9k_hw_spur_mitigate_freq)(struct ath_hw *ah,
                                            struct ath9k_channel *chan);
 
+       /* Private to hardware code */
+       struct ath_hw_private_ops private_ops;
+       /* Accessed by the lower level driver */
+       struct ath_hw_ops ops;
+
        /* Used to program the radio on non single-chip devices */
        u32 *analogBank0Data;
        u32 *analogBank1Data;
@@ -619,6 +654,16 @@ static inline struct ath_regulatory *ath9k_hw_regulatory(struct ath_hw *ah)
        return &(ath9k_hw_common(ah)->regulatory);
 }
 
+static inline struct ath_hw_private_ops *ath9k_hw_private_ops(struct ath_hw *ah)
+{
+       return &ah->private_ops;
+}
+
+static inline struct ath_hw_ops *ath9k_hw_ops(struct ath_hw *ah)
+{
+       return &ah->ops;
+}
+
 /* Initialization, Detach, Reset */
 const char *ath9k_hw_probe(u16 vendorid, u16 devid);
 void ath9k_hw_deinit(struct ath_hw *ah);
@@ -681,8 +726,6 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
 
 bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode);
 
-void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore, int power_off);
-
 /* Interrupt Handling */
 bool ath9k_hw_intrpend(struct ath_hw *ah);
 bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked);
index 6063f54..62682cc 100644 (file)
@@ -566,13 +566,10 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
        ath_read_cachesize(common, &csz);
        common->cachelsz = csz << 2; /* convert to bytes */
 
+       /* Initializes the hardware for all supported chipsets */
        ret = ath9k_hw_init(ah);
-       if (ret) {
-               ath_print(common, ATH_DBG_FATAL,
-                         "Unable to initialize hardware; "
-                         "initialization status: %d\n", ret);
+       if (ret)
                goto err_hw;
-       }
 
        ret = ath9k_init_debug(ah);
        if (ret) {