ath9k: move PCI code into separate file
authorGabor Juhos <juhosg@openwrt.org>
Wed, 14 Jan 2009 19:17:06 +0000 (20:17 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 29 Jan 2009 21:00:31 +0000 (16:00 -0500)
Now that we have converted all bus specific routines to replaceable, we
can move the PCI specific codes into a separate file.

Changes-licensed-under: ISC

Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
Tested-by: Pavel Roskin <proski@gnu.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath9k/Makefile
drivers/net/wireless/ath9k/core.h
drivers/net/wireless/ath9k/main.c
drivers/net/wireless/ath9k/pci.c [new file with mode: 0644]

index 1209d14613ac49cbdabf8e10fd17cf4257e2b440..af3f39bbddd5ce92d7ba0d400a457b8c41e36f5e 100644 (file)
@@ -11,6 +11,7 @@ ath9k-y +=    hw.o \
                xmit.o \
                rc.o
 
+ath9k-$(CONFIG_PCI) += pci.o
 ath9k-$(CONFIG_ATH9K_DEBUG) += debug.o
 
 obj-$(CONFIG_ATH9K) += ath9k.o
index f9fa5c64c77bbc9656ce8a97c3237c1dda844af4..1e86a9cbe42b49fcdc36a6e5a18f9cc34443594e 100644 (file)
@@ -18,7 +18,7 @@
 #define CORE_H
 
 #include <linux/etherdevice.h>
-#include <linux/pci.h>
+#include <linux/device.h>
 #include <net/mac80211.h>
 #include <linux/leds.h>
 #include <linux/rfkill.h>
@@ -767,4 +767,21 @@ static inline void ath_bus_cleanup(struct ath_softc *sc)
        sc->bus_ops->cleanup(sc);
 }
 
+extern struct ieee80211_ops ath9k_ops;
+
+irqreturn_t ath_isr(int irq, void *dev);
+void ath_cleanup(struct ath_softc *sc);
+int ath_attach(u16 devid, struct ath_softc *sc);
+void ath_detach(struct ath_softc *sc);
+const char *ath_mac_bb_name(u32 mac_bb_version);
+const char *ath_rf_name(u16 rf_version);
+
+#ifdef CONFIG_PCI
+int ath_pci_init(void);
+void ath_pci_exit(void);
+#else
+static inline int ath_pci_init(void) { return 0; };
+static inline void ath_pci_exit(void) {};
+#endif
+
 #endif /* CORE_H */
index dd2be2644cadd6b54c0dc2e6b4fbdf9f2151f713..6257790e49da045b3c8544b2bc734d253afd6a2a 100644 (file)
@@ -28,39 +28,6 @@ MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards.");
 MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards");
 MODULE_LICENSE("Dual BSD/GPL");
 
-static struct pci_device_id ath_pci_id_table[] __devinitdata = {
-       { PCI_VDEVICE(ATHEROS, 0x0023) }, /* PCI   */
-       { PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */
-       { PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI   */
-       { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI   */
-       { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
-       { PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */
-       { 0 }
-};
-
-static void ath_detach(struct ath_softc *sc);
-static void ath_cleanup(struct ath_softc *sc);
-
-/* return bus cachesize in 4B word units */
-
-static void ath_pci_read_cachesize(struct ath_softc *sc, int *csz)
-{
-       u8 u8tmp;
-
-       pci_read_config_byte(to_pci_dev(sc->dev), PCI_CACHE_LINE_SIZE,
-                            (u8 *)&u8tmp);
-       *csz = (int)u8tmp;
-
-       /*
-        * This check was put in to avoid "unplesant" consequences if
-        * the bootrom has not fully initialized all PCI devices.
-        * Sometimes the cache line size register is not set
-        */
-
-       if (*csz == 0)
-               *csz = DEFAULT_CACHELINE >> 2;   /* Use the default size */
-}
-
 static void ath_cache_conf_rate(struct ath_softc *sc,
                                struct ieee80211_conf *conf)
 {
@@ -500,7 +467,7 @@ static void ath9k_tasklet(unsigned long data)
        ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask);
 }
 
-static irqreturn_t ath_isr(int irq, void *dev)
+irqreturn_t ath_isr(int irq, void *dev)
 {
        struct ath_softc *sc = dev;
        struct ath_hal *ah = sc->sc_ah;
@@ -1279,7 +1246,7 @@ static int ath_start_rfkill_poll(struct ath_softc *sc)
 }
 #endif /* CONFIG_RFKILL */
 
-static void ath_cleanup(struct ath_softc *sc)
+void ath_cleanup(struct ath_softc *sc)
 {
        ath_detach(sc);
        free_irq(sc->irq, sc);
@@ -1287,7 +1254,7 @@ static void ath_cleanup(struct ath_softc *sc)
        ieee80211_free_hw(sc->hw);
 }
 
-static void ath_detach(struct ath_softc *sc)
+void ath_detach(struct ath_softc *sc)
 {
        struct ieee80211_hw *hw = sc->hw;
        int i = 0;
@@ -1541,7 +1508,7 @@ bad:
        return error;
 }
 
-static int ath_attach(u16 devid, struct ath_softc *sc)
+int ath_attach(u16 devid, struct ath_softc *sc)
 {
        struct ieee80211_hw *hw = sc->hw;
        int error = 0;
@@ -2462,7 +2429,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
        return ret;
 }
 
-static struct ieee80211_ops ath9k_ops = {
+struct ieee80211_ops ath9k_ops = {
        .tx                 = ath9k_tx,
        .start              = ath9k_start,
        .stop               = ath9k_stop,
@@ -2506,7 +2473,7 @@ static struct {
 /*
  * Return the MAC/BB name. "????" is returned if the MAC/BB is unknown.
  */
-static const char *
+const char *
 ath_mac_bb_name(u32 mac_bb_version)
 {
        int i;
@@ -2523,7 +2490,7 @@ ath_mac_bb_name(u32 mac_bb_version)
 /*
  * Return the RF name. "????" is returned if the RF is unknown.
  */
-static const char *
+const char *
 ath_rf_name(u16 rf_version)
 {
        int i;
@@ -2537,234 +2504,7 @@ ath_rf_name(u16 rf_version)
        return "????";
 }
 
-static void ath_pci_cleanup(struct ath_softc *sc)
-{
-       struct pci_dev *pdev = to_pci_dev(sc->dev);
-
-       pci_iounmap(pdev, sc->mem);
-       pci_release_region(pdev, 0);
-       pci_disable_device(pdev);
-}
-
-static struct ath_bus_ops ath_pci_bus_ops = {
-       .read_cachesize = ath_pci_read_cachesize,
-       .cleanup = ath_pci_cleanup,
-};
-
-static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
-       void __iomem *mem;
-       struct ath_softc *sc;
-       struct ieee80211_hw *hw;
-       u8 csz;
-       u32 val;
-       int ret = 0;
-       struct ath_hal *ah;
-
-       if (pci_enable_device(pdev))
-               return -EIO;
-
-       ret =  pci_set_dma_mask(pdev, DMA_32BIT_MASK);
-
-       if (ret) {
-               printk(KERN_ERR "ath9k: 32-bit DMA not available\n");
-               goto bad;
-       }
-
-       ret = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
-
-       if (ret) {
-               printk(KERN_ERR "ath9k: 32-bit DMA consistent "
-                       "DMA enable failed\n");
-               goto bad;
-       }
-
-       /*
-        * 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 / sizeof(u32);
-               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);
-
-       pci_set_master(pdev);
-
-       /*
-        * Disable the RETRY_TIMEOUT register (0x41) to keep
-        * PCI Tx retries from interfering with C3 CPU state.
-        */
-       pci_read_config_dword(pdev, 0x40, &val);
-       if ((val & 0x0000ff00) != 0)
-               pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
-
-       ret = pci_request_region(pdev, 0, "ath9k");
-       if (ret) {
-               dev_err(&pdev->dev, "PCI memory region reserve error\n");
-               ret = -ENODEV;
-               goto bad;
-       }
-
-       mem = pci_iomap(pdev, 0, 0);
-       if (!mem) {
-               printk(KERN_ERR "PCI memory map error\n") ;
-               ret = -EIO;
-               goto bad1;
-       }
-
-       hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
-       if (hw == NULL) {
-               printk(KERN_ERR "ath_pci: no memory for ieee80211_hw\n");
-               goto bad2;
-       }
-
-       SET_IEEE80211_DEV(hw, &pdev->dev);
-       pci_set_drvdata(pdev, hw);
-
-       sc = hw->priv;
-       sc->hw = hw;
-       sc->dev = &pdev->dev;
-       sc->mem = mem;
-       sc->bus_ops = &ath_pci_bus_ops;
-
-       if (ath_attach(id->device, sc) != 0) {
-               ret = -ENODEV;
-               goto bad3;
-       }
-
-       /* setup interrupt service routine */
-
-       if (request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath", sc)) {
-               printk(KERN_ERR "%s: request_irq failed\n",
-                       wiphy_name(hw->wiphy));
-               ret = -EIO;
-               goto bad4;
-       }
-
-       sc->irq = pdev->irq;
-
-       ah = sc->sc_ah;
-       printk(KERN_INFO
-              "%s: Atheros AR%s MAC/BB Rev:%x "
-              "AR%s RF Rev:%x: mem=0x%lx, irq=%d\n",
-              wiphy_name(hw->wiphy),
-              ath_mac_bb_name(ah->ah_macVersion),
-              ah->ah_macRev,
-              ath_rf_name((ah->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR)),
-              ah->ah_phyRev,
-              (unsigned long)mem, pdev->irq);
-
-       return 0;
-bad4:
-       ath_detach(sc);
-bad3:
-       ieee80211_free_hw(hw);
-bad2:
-       pci_iounmap(pdev, mem);
-bad1:
-       pci_release_region(pdev, 0);
-bad:
-       pci_disable_device(pdev);
-       return ret;
-}
-
-static void ath_pci_remove(struct pci_dev *pdev)
-{
-       struct ieee80211_hw *hw = pci_get_drvdata(pdev);
-       struct ath_softc *sc = hw->priv;
-
-       ath_cleanup(sc);
-}
-
-#ifdef CONFIG_PM
-
-static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)
-{
-       struct ieee80211_hw *hw = pci_get_drvdata(pdev);
-       struct ath_softc *sc = hw->priv;
-
-       ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
-
-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
-       if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
-               cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
-#endif
-
-       pci_save_state(pdev);
-       pci_disable_device(pdev);
-       pci_set_power_state(pdev, PCI_D3hot);
-
-       return 0;
-}
-
-static int ath_pci_resume(struct pci_dev *pdev)
-{
-       struct ieee80211_hw *hw = pci_get_drvdata(pdev);
-       struct ath_softc *sc = hw->priv;
-       u32 val;
-       int err;
-
-       err = pci_enable_device(pdev);
-       if (err)
-               return err;
-       pci_restore_state(pdev);
-       /*
-        * 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_read_config_dword(pdev, 0x40, &val);
-       if ((val & 0x0000ff00) != 0)
-               pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
-
-       /* Enable LED */
-       ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN,
-                           AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
-       ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
-
-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
-       /*
-        * check the h/w rfkill state on resume
-        * and start the rfkill poll timer
-        */
-       if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
-               queue_delayed_work(sc->hw->workqueue,
-                                  &sc->rf_kill.rfkill_poll, 0);
-#endif
-
-       return 0;
-}
-
-#endif /* CONFIG_PM */
-
-MODULE_DEVICE_TABLE(pci, ath_pci_id_table);
-
-static struct pci_driver ath_pci_driver = {
-       .name       = "ath9k",
-       .id_table   = ath_pci_id_table,
-       .probe      = ath_pci_probe,
-       .remove     = ath_pci_remove,
-#ifdef CONFIG_PM
-       .suspend    = ath_pci_suspend,
-       .resume     = ath_pci_resume,
-#endif /* CONFIG_PM */
-};
-
-static int __init init_ath_pci(void)
+static int __init ath9k_init(void)
 {
        int error;
 
@@ -2776,26 +2516,30 @@ static int __init init_ath_pci(void)
                printk(KERN_ERR
                        "Unable to register rate control algorithm: %d\n",
                        error);
-               ath_rate_control_unregister();
-               return error;
+               goto err_out;
        }
 
-       if (pci_register_driver(&ath_pci_driver) < 0) {
+       error = ath_pci_init();
+       if (error < 0) {
                printk(KERN_ERR
                        "ath_pci: No devices found, driver not installed.\n");
-               ath_rate_control_unregister();
-               pci_unregister_driver(&ath_pci_driver);
-               return -ENODEV;
+               error = -ENODEV;
+               goto err_rate_unregister;
        }
 
        return 0;
+
+ err_rate_unregister:
+       ath_rate_control_unregister();
+ err_out:
+       return error;
 }
-module_init(init_ath_pci);
+module_init(ath9k_init);
 
-static void __exit exit_ath_pci(void)
+static void __exit ath9k_exit(void)
 {
+       ath_pci_exit();
        ath_rate_control_unregister();
-       pci_unregister_driver(&ath_pci_driver);
        printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
 }
-module_exit(exit_ath_pci);
+module_exit(ath9k_exit);
diff --git a/drivers/net/wireless/ath9k/pci.c b/drivers/net/wireless/ath9k/pci.c
new file mode 100644 (file)
index 0000000..4ff1caa
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2008 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 "core.h"
+#include "reg.h"
+#include "hw.h"
+
+static struct pci_device_id ath_pci_id_table[] __devinitdata = {
+       { PCI_VDEVICE(ATHEROS, 0x0023) }, /* PCI   */
+       { PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */
+       { PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI   */
+       { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI   */
+       { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
+       { PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */
+       { 0 }
+};
+
+/* return bus cachesize in 4B word units */
+static void ath_pci_read_cachesize(struct ath_softc *sc, int *csz)
+{
+       u8 u8tmp;
+
+       pci_read_config_byte(to_pci_dev(sc->dev), PCI_CACHE_LINE_SIZE,
+                            (u8 *)&u8tmp);
+       *csz = (int)u8tmp;
+
+       /*
+        * This check was put in to avoid "unplesant" consequences if
+        * the bootrom has not fully initialized all PCI devices.
+        * Sometimes the cache line size register is not set
+        */
+
+       if (*csz == 0)
+               *csz = DEFAULT_CACHELINE >> 2;   /* Use the default size */
+}
+
+static void ath_pci_cleanup(struct ath_softc *sc)
+{
+       struct pci_dev *pdev = to_pci_dev(sc->dev);
+
+       pci_iounmap(pdev, sc->mem);
+       pci_release_region(pdev, 0);
+       pci_disable_device(pdev);
+}
+
+static struct ath_bus_ops ath_pci_bus_ops = {
+       .read_cachesize = ath_pci_read_cachesize,
+       .cleanup = ath_pci_cleanup,
+};
+
+static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+       void __iomem *mem;
+       struct ath_softc *sc;
+       struct ieee80211_hw *hw;
+       u8 csz;
+       u32 val;
+       int ret = 0;
+       struct ath_hal *ah;
+
+       if (pci_enable_device(pdev))
+               return -EIO;
+
+       ret =  pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+
+       if (ret) {
+               printk(KERN_ERR "ath9k: 32-bit DMA not available\n");
+               goto bad;
+       }
+
+       ret = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+
+       if (ret) {
+               printk(KERN_ERR "ath9k: 32-bit DMA consistent "
+                       "DMA enable failed\n");
+               goto bad;
+       }
+
+       /*
+        * 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 / sizeof(u32);
+               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);
+
+       pci_set_master(pdev);
+
+       /*
+        * Disable the RETRY_TIMEOUT register (0x41) to keep
+        * PCI Tx retries from interfering with C3 CPU state.
+        */
+       pci_read_config_dword(pdev, 0x40, &val);
+       if ((val & 0x0000ff00) != 0)
+               pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+
+       ret = pci_request_region(pdev, 0, "ath9k");
+       if (ret) {
+               dev_err(&pdev->dev, "PCI memory region reserve error\n");
+               ret = -ENODEV;
+               goto bad;
+       }
+
+       mem = pci_iomap(pdev, 0, 0);
+       if (!mem) {
+               printk(KERN_ERR "PCI memory map error\n") ;
+               ret = -EIO;
+               goto bad1;
+       }
+
+       hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
+       if (hw == NULL) {
+               printk(KERN_ERR "ath_pci: no memory for ieee80211_hw\n");
+               goto bad2;
+       }
+
+       SET_IEEE80211_DEV(hw, &pdev->dev);
+       pci_set_drvdata(pdev, hw);
+
+       sc = hw->priv;
+       sc->hw = hw;
+       sc->dev = &pdev->dev;
+       sc->mem = mem;
+       sc->bus_ops = &ath_pci_bus_ops;
+
+       if (ath_attach(id->device, sc) != 0) {
+               ret = -ENODEV;
+               goto bad3;
+       }
+
+       /* setup interrupt service routine */
+
+       if (request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath", sc)) {
+               printk(KERN_ERR "%s: request_irq failed\n",
+                       wiphy_name(hw->wiphy));
+               ret = -EIO;
+               goto bad4;
+       }
+
+       sc->irq = pdev->irq;
+
+       ah = sc->sc_ah;
+       printk(KERN_INFO
+              "%s: Atheros AR%s MAC/BB Rev:%x "
+              "AR%s RF Rev:%x: mem=0x%lx, irq=%d\n",
+              wiphy_name(hw->wiphy),
+              ath_mac_bb_name(ah->ah_macVersion),
+              ah->ah_macRev,
+              ath_rf_name((ah->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR)),
+              ah->ah_phyRev,
+              (unsigned long)mem, pdev->irq);
+
+       return 0;
+bad4:
+       ath_detach(sc);
+bad3:
+       ieee80211_free_hw(hw);
+bad2:
+       pci_iounmap(pdev, mem);
+bad1:
+       pci_release_region(pdev, 0);
+bad:
+       pci_disable_device(pdev);
+       return ret;
+}
+
+static void ath_pci_remove(struct pci_dev *pdev)
+{
+       struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+       struct ath_softc *sc = hw->priv;
+
+       ath_cleanup(sc);
+}
+
+#ifdef CONFIG_PM
+
+static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+       struct ath_softc *sc = hw->priv;
+
+       ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
+
+#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
+       if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
+               cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
+#endif
+
+       pci_save_state(pdev);
+       pci_disable_device(pdev);
+       pci_set_power_state(pdev, PCI_D3hot);
+
+       return 0;
+}
+
+static int ath_pci_resume(struct pci_dev *pdev)
+{
+       struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+       struct ath_softc *sc = hw->priv;
+       u32 val;
+       int err;
+
+       err = pci_enable_device(pdev);
+       if (err)
+               return err;
+       pci_restore_state(pdev);
+       /*
+        * 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_read_config_dword(pdev, 0x40, &val);
+       if ((val & 0x0000ff00) != 0)
+               pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+
+       /* Enable LED */
+       ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN,
+                           AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+       ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
+
+#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
+       /*
+        * check the h/w rfkill state on resume
+        * and start the rfkill poll timer
+        */
+       if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
+               queue_delayed_work(sc->hw->workqueue,
+                                  &sc->rf_kill.rfkill_poll, 0);
+#endif
+
+       return 0;
+}
+
+#endif /* CONFIG_PM */
+
+MODULE_DEVICE_TABLE(pci, ath_pci_id_table);
+
+static struct pci_driver ath_pci_driver = {
+       .name       = "ath9k",
+       .id_table   = ath_pci_id_table,
+       .probe      = ath_pci_probe,
+       .remove     = ath_pci_remove,
+#ifdef CONFIG_PM
+       .suspend    = ath_pci_suspend,
+       .resume     = ath_pci_resume,
+#endif /* CONFIG_PM */
+};
+
+int __init ath_pci_init(void)
+{
+       return pci_register_driver(&ath_pci_driver);
+}
+
+void ath_pci_exit(void)
+{
+       pci_unregister_driver(&ath_pci_driver);
+}