Commit 6baff7f9 authored by Gabor Juhos's avatar Gabor Juhos Committed by John W. Linville

ath9k: move PCI code into separate file

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: default avatarGabor Juhos <juhosg@openwrt.org>
Signed-off-by: default avatarImre Kaloz <kaloz@openwrt.org>
Tested-by: default avatarPavel Roskin <proski@gnu.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 39c3c2f2
...@@ -11,6 +11,7 @@ ath9k-y += hw.o \ ...@@ -11,6 +11,7 @@ ath9k-y += hw.o \
xmit.o \ xmit.o \
rc.o rc.o
ath9k-$(CONFIG_PCI) += pci.o
ath9k-$(CONFIG_ATH9K_DEBUG) += debug.o ath9k-$(CONFIG_ATH9K_DEBUG) += debug.o
obj-$(CONFIG_ATH9K) += ath9k.o obj-$(CONFIG_ATH9K) += ath9k.o
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#define CORE_H #define CORE_H
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/pci.h> #include <linux/device.h>
#include <net/mac80211.h> #include <net/mac80211.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/rfkill.h> #include <linux/rfkill.h>
...@@ -767,4 +767,21 @@ static inline void ath_bus_cleanup(struct ath_softc *sc) ...@@ -767,4 +767,21 @@ static inline void ath_bus_cleanup(struct ath_softc *sc)
sc->bus_ops->cleanup(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 */ #endif /* CORE_H */
...@@ -28,39 +28,6 @@ MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards."); ...@@ -28,39 +28,6 @@ MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards.");
MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards"); MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards");
MODULE_LICENSE("Dual BSD/GPL"); 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, static void ath_cache_conf_rate(struct ath_softc *sc,
struct ieee80211_conf *conf) struct ieee80211_conf *conf)
{ {
...@@ -500,7 +467,7 @@ static void ath9k_tasklet(unsigned long data) ...@@ -500,7 +467,7 @@ static void ath9k_tasklet(unsigned long data)
ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask); 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_softc *sc = dev;
struct ath_hal *ah = sc->sc_ah; struct ath_hal *ah = sc->sc_ah;
...@@ -1279,7 +1246,7 @@ static int ath_start_rfkill_poll(struct ath_softc *sc) ...@@ -1279,7 +1246,7 @@ static int ath_start_rfkill_poll(struct ath_softc *sc)
} }
#endif /* CONFIG_RFKILL */ #endif /* CONFIG_RFKILL */
static void ath_cleanup(struct ath_softc *sc) void ath_cleanup(struct ath_softc *sc)
{ {
ath_detach(sc); ath_detach(sc);
free_irq(sc->irq, sc); free_irq(sc->irq, sc);
...@@ -1287,7 +1254,7 @@ static void ath_cleanup(struct ath_softc *sc) ...@@ -1287,7 +1254,7 @@ static void ath_cleanup(struct ath_softc *sc)
ieee80211_free_hw(sc->hw); 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; struct ieee80211_hw *hw = sc->hw;
int i = 0; int i = 0;
...@@ -1541,7 +1508,7 @@ static int ath_init(u16 devid, struct ath_softc *sc) ...@@ -1541,7 +1508,7 @@ static int ath_init(u16 devid, struct ath_softc *sc)
return error; 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; struct ieee80211_hw *hw = sc->hw;
int error = 0; int error = 0;
...@@ -2462,7 +2429,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, ...@@ -2462,7 +2429,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
return ret; return ret;
} }
static struct ieee80211_ops ath9k_ops = { struct ieee80211_ops ath9k_ops = {
.tx = ath9k_tx, .tx = ath9k_tx,
.start = ath9k_start, .start = ath9k_start,
.stop = ath9k_stop, .stop = ath9k_stop,
...@@ -2506,7 +2473,7 @@ static struct { ...@@ -2506,7 +2473,7 @@ static struct {
/* /*
* Return the MAC/BB name. "????" is returned if the MAC/BB is unknown. * 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) ath_mac_bb_name(u32 mac_bb_version)
{ {
int i; int i;
...@@ -2523,7 +2490,7 @@ ath_mac_bb_name(u32 mac_bb_version) ...@@ -2523,7 +2490,7 @@ ath_mac_bb_name(u32 mac_bb_version)
/* /*
* Return the RF name. "????" is returned if the RF is unknown. * Return the RF name. "????" is returned if the RF is unknown.
*/ */
static const char * const char *
ath_rf_name(u16 rf_version) ath_rf_name(u16 rf_version)
{ {
int i; int i;
...@@ -2537,234 +2504,7 @@ ath_rf_name(u16 rf_version) ...@@ -2537,234 +2504,7 @@ ath_rf_name(u16 rf_version)
return "????"; return "????";
} }
static void ath_pci_cleanup(struct ath_softc *sc) static int __init ath9k_init(void)
{
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)
{ {
int error; int error;
...@@ -2776,26 +2516,30 @@ static int __init init_ath_pci(void) ...@@ -2776,26 +2516,30 @@ static int __init init_ath_pci(void)
printk(KERN_ERR printk(KERN_ERR
"Unable to register rate control algorithm: %d\n", "Unable to register rate control algorithm: %d\n",
error); error);
ath_rate_control_unregister(); goto err_out;
return error;
} }
if (pci_register_driver(&ath_pci_driver) < 0) { error = ath_pci_init();
if (error < 0) {
printk(KERN_ERR printk(KERN_ERR
"ath_pci: No devices found, driver not installed.\n"); "ath_pci: No devices found, driver not installed.\n");
ath_rate_control_unregister(); error = -ENODEV;
pci_unregister_driver(&ath_pci_driver); goto err_rate_unregister;
return -ENODEV;
} }
return 0; 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(); ath_rate_control_unregister();
pci_unregister_driver(&ath_pci_driver);
printk(KERN_INFO "%s: Driver unloaded\n", dev_info); printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
} }
module_exit(exit_ath_pci); module_exit(ath9k_exit);
/*
* 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);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment