aboutsummaryrefslogtreecommitdiffstats
path: root/package/mac80211/patches/405-ath9k-move-PCI-code-into-separate-file.patch
blob: 4a55ded9e79e8090a3966469ee73277671e6bea3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
1
# -*- mode: Makefile; -*-

# A debug build of Xen and tools?
debug ?= y

XEN_COMPILE_ARCH    ?= $(shell uname -m | sed -e s/i.86/x86_32/ \
                         -e s/i86pc/x86_32/ -e s/amd64/x86_64/)
XEN_TARGET_ARCH     ?= $(XEN_COMPILE_ARCH)
XEN_OS              ?= $(shell uname -s)

CONFIG_$(XEN_OS) := y

SHELL     ?= /bin/sh

# Tools to run on system hosting the build
HOSTCC      = gcc
HOSTCFLAGS  = -Wall -Werror -Wstrict-prototypes -O2 -fomit-frame-pointer
HOSTCFLAGS += -fno-strict-aliasing

DISTDIR     ?= $(XEN_ROOT)/dist
DESTDIR     ?= /

# Allow phony attribute to be listed as dependency rather than fake target
.PHONY: .phony

include $(XEN_ROOT)/config/$(XEN_OS).mk
include $(XEN_ROOT)/config/$(XEN_TARGET_ARCH).mk

SHAREDIR    ?= $(PREFIX)/share
DOCDIR      ?= $(SHAREDIR)/doc/xen
MANDIR      ?= $(SHAREDIR)/man
BASH_COMPLETION_DIR ?= $(CONFIG_DIR)/bash_completion.d

# arguments: variable, common path part, path to test, if yes, if no
define setvar_dir
  ifndef $(1)
    ifneq (,$(wildcard $(2)$(3)))
      $(1) ?= $(2)$(4)
    else
      $(1) ?= $(2)$(5)
    endif
  endif
endef

# See distro_mapping.txt for other options
$(eval $(call setvar_dir,CONFIG_LEAF_DIR,,/etc/sysconfig,sysconfig,default))
$(eval $(call setvar_dir,SUBSYS_DIR,/var/run,/subsys,/subsys,))
$(eval $(call setvar_dir,INITD_DIR,/etc,/rc.d/init.d,/rc.d/init.d,/init.d))

ifneq ($(EXTRA_PREFIX),)
EXTRA_INCLUDES += $(EXTRA_PREFIX)/include
EXTRA_LIB += $(EXTRA_PREFIX)/$(LIBLEAFDIR)
endif

BISON	?= bison
FLEX	?= flex

PYTHON      ?= python
PYTHON_PREFIX_ARG ?= --prefix="$(PREFIX)"
# The above requires that PREFIX contains *no spaces*. This variable is here
# to permit the user to set PYTHON_PREFIX_ARG to '' to workaround this bug:
#  https://bugs.launchpad.net/ubuntu/+bug/362570

# cc-option: Check if compiler supports first option, else fall back to second.
# Usage: cflags-y += $(call cc-option,$(CC),-march=winchip-c6,-march=i586)
cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc \
              /dev/null 2>&1`"; then echo "$(2)"; else echo "$(3)"; fi ;)

# cc-option-add: Add an option to compilation flags, but only if supported.
# Usage: $(call cc-option-add CFLAGS,CC,-march=winchip-c6)
cc-option-add = $(eval $(call cc-option-add-closure,$(1),$(2),$(3)))
define cc-option-add-closure
    ifneq ($$(call cc-option,$$($(2)),$(3),n),n)
        $(1) += $(3)
    endif
endef

cc-options-add = $(foreach o,$(3),$(call cc-option-add,$(1),$(2),$(o)))

# cc-ver: Check compiler is at least specified version. Return boolean 'y'/'n'.
# Usage: ifeq ($(call cc-ver,$(CC),0x030400),y)
cc-ver = $(shell if [ $$((`$(1) -dumpversion | awk -F. \
           '{ printf "0x%02x%02x%02x", $$1, $$2, $$3}'`)) -ge $$(($(2))) ]; \
           then echo y; else echo n; fi ;)

# cc-ver-check: Check compiler is at least specified version, else fail.
# Usage: $(call cc-ver-check,CC,0x030400,"Require at least gcc-3.4")
cc-ver-check = $(eval $(call cc-ver-check-closure,$(1),$(2),$(3)))
define cc-ver-check-closure
    ifeq ($$(call cc-ver,$$($(1)),$(2)),n)
        override $(1) = echo "*** FATAL BUILD ERROR: "$(3) >&2; exit 1;
        cc-option := n
    endif
endef

define absolutify_xen_root
    case "$(XEN_ROOT)" in                                          \
    /*) XEN_ROOT=$(XEN_ROOT) ;;                                    \
    *)  xen_root_lhs=`pwd`;                                        \
        xen_root_rhs=$(XEN_ROOT)/;                                 \
        while [ "x$${xen_root_rhs#../}" != "x$$xen_root_rhs" ]; do \
            xen_root_rhs="$${xen_root_rhs#../}";                   \
            xen_root_rhs="$${xen_root_rhs#/}";                     \
            xen_root_rhs="$${xen_root_rhs#/}";                     \
            xen_root_lhs="$${xen_root_lhs%/*}";                    \
        done;                                                      \
        XEN_ROOT="$$xen_root_lhs/$$xen_root_rhs" ;;                \
    esac;                                                          \
    export XEN_ROOT
endef

define buildmakevars2shellvars
    PREFIX="$(PREFIX)";                                            \
    XEN_SCRIPT_DIR="$(XEN_SCRIPT_DIR)";                            \
    export PREFIX;                                                 \
    export XEN_SCRIPT_DIR
endef

buildmakevars2file = $(eval $(call buildmakevars2file-
From 4803193b165c67abcd7cb10dd0c178dbcffb6cc7 Mon Sep 17 00:00:00 2001
From: Gabor Juhos <juhosg@openwrt.org>
Date: Mon, 5 Jan 2009 11:01:09 +0100
Subject: [PATCH v3 05/11] 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>
---
 drivers/net/wireless/ath9k/Makefile |    1 +
 drivers/net/wireless/ath9k/core.h   |   19 ++-
 drivers/net/wireless/ath9k/main.c   |  300 +++--------------------------------
 drivers/net/wireless/ath9k/pci.c    |  287 +++++++++++++++++++++++++++++++++
 4 files changed, 328 insertions(+), 279 deletions(-)
 create mode 100644 drivers/net/wireless/ath9k/pci.c

--- a/drivers/net/wireless/ath9k/Makefile
+++ b/drivers/net/wireless/ath9k/Makefile
@@ -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
--- a/drivers/net/wireless/ath9k/core.h
+++ b/drivers/net/wireless/ath9k/core.h
@@ -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(struc
 	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 */
--- a/drivers/net/wireless/ath9k/main.c
+++ b/drivers/net/wireless/ath9k/main.c
@@ -28,39 +28,6 @@ MODULE_DESCRIPTION("Support for Atheros 
 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 
 	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;
@@ -1281,7 +1248,7 @@ static int ath_start_rfkill_poll(struct 
 }
 #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);
@@ -1289,7 +1256,7 @@ static void ath_cleanup(struct ath_softc
 	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;
@@ -1543,7 +1510,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;
@@ -2466,7 +2433,7 @@ static int ath9k_ampdu_action(struct iee
 	return ret;
 }
 
-static struct ieee80211_ops ath9k_ops = {
+struct ieee80211_ops ath9k_ops = {
 	.tx 		    = ath9k_tx,
 	.start 		    = ath9k_start,
 	.stop 		    = ath9k_stop,
@@ -2510,7 +2477,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;
@@ -2527,7 +2494,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;
@@ -2541,234 +2508,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, 3);
-
-	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;
 
@@ -2780,26 +2520,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);
--- /dev/null
+++ b/drivers/net/wireless/ath9k/pci.c
@@ -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, 3);
+
+	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);
+}