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: 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>
This commit is contained in:
Родитель
39c3c2f2de
Коммит
6baff7f9a6
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
module_init(init_ath_pci);
|
||||
|
||||
static void __exit exit_ath_pci(void)
|
||||
{
|
||||
err_rate_unregister:
|
||||
ath_rate_control_unregister();
|
||||
err_out:
|
||||
return error;
|
||||
}
|
||||
module_init(ath9k_init);
|
||||
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
}
|
Загрузка…
Ссылка в новой задаче