AR_GPIO_BIT(gpio));
 }
 
-static u32 ath9k_hw_gpio_get(struct ath_hal *ah, u32 gpio)
+/*
+ * Configure GPIO Input lines
+ */
+void ath9k_hw_cfg_gpio_input(struct ath_hal *ah, u32 gpio)
+{
+       u32 gpio_shift;
+
+       ASSERT(gpio < ah->ah_caps.num_gpio_pins);
+
+       gpio_shift = gpio << 1;
+
+       REG_RMW(ah,
+               AR_GPIO_OE_OUT,
+               (AR_GPIO_OE_OUT_DRV_NO << gpio_shift),
+               (AR_GPIO_OE_OUT_DRV << gpio_shift));
+}
+
+#ifdef CONFIG_RFKILL
+static void ath9k_enable_rfkill(struct ath_hal *ah)
+{
+       REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL,
+                   AR_GPIO_INPUT_EN_VAL_RFSILENT_BB);
+
+       REG_CLR_BIT(ah, AR_GPIO_INPUT_MUX2,
+                   AR_GPIO_INPUT_MUX2_RFSILENT);
+
+       ath9k_hw_cfg_gpio_input(ah, ah->ah_rfkill_gpio);
+       REG_SET_BIT(ah, AR_PHY_TEST, RFSILENT_BB);
+}
+#endif
+
+u32 ath9k_hw_gpio_get(struct ath_hal *ah, u32 gpio)
 {
        if (gpio >= ah->ah_caps.num_gpio_pins)
                return 0xffffffff;
 
        pCap->hw_caps |= ATH9K_HW_CAP_ENHANCEDPM;
 
+#ifdef CONFIG_RFKILL
        ah->ah_rfsilent = ath9k_hw_get_eeprom(ahp, EEP_RF_SILENT);
        if (ah->ah_rfsilent & EEP_RFSILENT_ENABLED) {
-               ahp->ah_gpioSelect =
+               ah->ah_rfkill_gpio =
                        MS(ah->ah_rfsilent, EEP_RFSILENT_GPIO_SEL);
-               ahp->ah_polarity =
+               ah->ah_rfkill_polarity =
                        MS(ah->ah_rfsilent, EEP_RFSILENT_POLARITY);
 
-               ath9k_hw_setcapability(ah, ATH9K_CAP_RFSILENT, 1, true,
-                                      NULL);
                pCap->hw_caps |= ATH9K_HW_CAP_RFSILENT;
        }
+#endif
 
        if ((ah->ah_macVersion == AR_SREV_VERSION_5416_PCI) ||
            (ah->ah_macVersion == AR_SREV_VERSION_5416_PCIE) ||
        ath9k_hw_init_interrupt_masks(ah, ah->ah_opmode);
        ath9k_hw_init_qos(ah);
 
+#ifdef CONFIG_RFKILL
+       if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
+               ath9k_enable_rfkill(ah);
+#endif
        ath9k_hw_init_user_settings(ah);
 
        REG_WRITE(ah, AR_STA_ID1,
        return true;
 }
 
-#ifdef CONFIG_ATH9K_RFKILL
-static void ath9k_enable_rfkill(struct ath_hal *ah)
-{
-       struct ath_hal_5416 *ahp = AH5416(ah);
-
-       REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL,
-                   AR_GPIO_INPUT_EN_VAL_RFSILENT_BB);
-
-       REG_CLR_BIT(ah, AR_GPIO_INPUT_MUX2,
-                   AR_GPIO_INPUT_MUX2_RFSILENT);
-
-       ath9k_hw_cfg_gpio_input(ah, ahp->ah_gpioSelect);
-       REG_SET_BIT(ah, AR_PHY_TEST, RFSILENT_BB);
-
-       if (ahp->ah_gpioBit == ath9k_hw_gpio_get(ah, ahp->ah_gpioSelect)) {
-
-               ath9k_hw_set_gpio_intr(ah, ahp->ah_gpioSelect,
-                                      !ahp->ah_gpioBit);
-       } else {
-               ath9k_hw_set_gpio_intr(ah, ahp->ah_gpioSelect,
-                                      ahp->ah_gpioBit);
-       }
-}
-#endif
-
 void
 ath9k_hw_write_associd(struct ath_hal *ah, const u8 *bssid,
                       u16 assocId)
 
        ath_deinit_leds(sc);
 }
 
+#ifdef CONFIG_RFKILL
+/*******************/
+/*     Rfkill     */
+/*******************/
+
+static void ath_radio_enable(struct ath_softc *sc)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       int status;
+
+       spin_lock_bh(&sc->sc_resetlock);
+       if (!ath9k_hw_reset(ah, ah->ah_curchan,
+                           sc->sc_ht_info.tx_chan_width,
+                           sc->sc_tx_chainmask,
+                           sc->sc_rx_chainmask,
+                           sc->sc_ht_extprotspacing,
+                           false, &status)) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: unable to reset channel %u (%uMhz) "
+                       "flags 0x%x hal status %u\n", __func__,
+                       ath9k_hw_mhz2ieee(ah,
+                                         ah->ah_curchan->channel,
+                                         ah->ah_curchan->channelFlags),
+                       ah->ah_curchan->channel,
+                       ah->ah_curchan->channelFlags, status);
+       }
+       spin_unlock_bh(&sc->sc_resetlock);
+
+       ath_update_txpow(sc);
+       if (ath_startrecv(sc) != 0) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: unable to restart recv logic\n", __func__);
+               return;
+       }
+
+       if (sc->sc_flags & SC_OP_BEACONS)
+               ath_beacon_config(sc, ATH_IF_ID_ANY);   /* restart beacons */
+
+       /* Re-Enable  interrupts */
+       ath9k_hw_set_interrupts(ah, sc->sc_imask);
+
+       /* Enable LED */
+       ath9k_hw_cfg_output(ah, ATH_LED_PIN,
+                           AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+       ath9k_hw_set_gpio(ah, ATH_LED_PIN, 0);
+
+       ieee80211_wake_queues(sc->hw);
+}
+
+static void ath_radio_disable(struct ath_softc *sc)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       int status;
+
+
+       ieee80211_stop_queues(sc->hw);
+
+       /* Disable LED */
+       ath9k_hw_set_gpio(ah, ATH_LED_PIN, 1);
+       ath9k_hw_cfg_gpio_input(ah, ATH_LED_PIN);
+
+       /* Disable interrupts */
+       ath9k_hw_set_interrupts(ah, 0);
+
+       ath_draintxq(sc, false);        /* clear pending tx frames */
+       ath_stoprecv(sc);               /* turn off frame recv */
+       ath_flushrecv(sc);              /* flush recv queue */
+
+       spin_lock_bh(&sc->sc_resetlock);
+       if (!ath9k_hw_reset(ah, ah->ah_curchan,
+                           sc->sc_ht_info.tx_chan_width,
+                           sc->sc_tx_chainmask,
+                           sc->sc_rx_chainmask,
+                           sc->sc_ht_extprotspacing,
+                           false, &status)) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: unable to reset channel %u (%uMhz) "
+                       "flags 0x%x hal status %u\n", __func__,
+                       ath9k_hw_mhz2ieee(ah,
+                               ah->ah_curchan->channel,
+                               ah->ah_curchan->channelFlags),
+                       ah->ah_curchan->channel,
+                       ah->ah_curchan->channelFlags, status);
+       }
+       spin_unlock_bh(&sc->sc_resetlock);
+
+       ath9k_hw_phy_disable(ah);
+       ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP);
+}
+
+static bool ath_is_rfkill_set(struct ath_softc *sc)
+{
+       struct ath_hal *ah = sc->sc_ah;
+
+       return ath9k_hw_gpio_get(ah, ah->ah_rfkill_gpio) ==
+                                 ah->ah_rfkill_polarity;
+}
+
+/* h/w rfkill poll function */
+static void ath_rfkill_poll(struct work_struct *work)
+{
+       struct ath_softc *sc = container_of(work, struct ath_softc,
+                                           rf_kill.rfkill_poll.work);
+       bool radio_on;
+
+       if (sc->sc_flags & SC_OP_INVALID)
+               return;
+
+       radio_on = !ath_is_rfkill_set(sc);
+
+       /*
+        * enable/disable radio only when there is a
+        * state change in RF switch
+        */
+       if (radio_on == !!(sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED)) {
+               enum rfkill_state state;
+
+               if (sc->sc_flags & SC_OP_RFKILL_SW_BLOCKED) {
+                       state = radio_on ? RFKILL_STATE_SOFT_BLOCKED
+                               : RFKILL_STATE_HARD_BLOCKED;
+               } else if (radio_on) {
+                       ath_radio_enable(sc);
+                       state = RFKILL_STATE_UNBLOCKED;
+               } else {
+                       ath_radio_disable(sc);
+                       state = RFKILL_STATE_HARD_BLOCKED;
+               }
+
+               if (state == RFKILL_STATE_HARD_BLOCKED)
+                       sc->sc_flags |= SC_OP_RFKILL_HW_BLOCKED;
+               else
+                       sc->sc_flags &= ~SC_OP_RFKILL_HW_BLOCKED;
+
+               rfkill_force_state(sc->rf_kill.rfkill, state);
+       }
+
+       queue_delayed_work(sc->hw->workqueue, &sc->rf_kill.rfkill_poll,
+                          msecs_to_jiffies(ATH_RFKILL_POLL_INTERVAL));
+}
+
+/* s/w rfkill handler */
+static int ath_sw_toggle_radio(void *data, enum rfkill_state state)
+{
+       struct ath_softc *sc = data;
+
+       switch (state) {
+       case RFKILL_STATE_SOFT_BLOCKED:
+               if (!(sc->sc_flags & (SC_OP_RFKILL_HW_BLOCKED |
+                   SC_OP_RFKILL_SW_BLOCKED)))
+                       ath_radio_disable(sc);
+               sc->sc_flags |= SC_OP_RFKILL_SW_BLOCKED;
+               return 0;
+       case RFKILL_STATE_UNBLOCKED:
+               if ((sc->sc_flags & SC_OP_RFKILL_SW_BLOCKED)) {
+                       sc->sc_flags &= ~SC_OP_RFKILL_SW_BLOCKED;
+                       if (sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED) {
+                               DPRINTF(sc, ATH_DBG_FATAL, "Can't turn on the"
+                                       "radio as it is disabled by h/w \n");
+                               return -EPERM;
+                       }
+                       ath_radio_enable(sc);
+               }
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+/* Init s/w rfkill */
+static int ath_init_sw_rfkill(struct ath_softc *sc)
+{
+       sc->rf_kill.rfkill = rfkill_allocate(wiphy_dev(sc->hw->wiphy),
+                                            RFKILL_TYPE_WLAN);
+       if (!sc->rf_kill.rfkill) {
+               DPRINTF(sc, ATH_DBG_FATAL, "Failed to allocate rfkill\n");
+               return -ENOMEM;
+       }
+
+       snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name),
+               "ath9k-%s:rfkill", wiphy_name(sc->hw->wiphy));
+       sc->rf_kill.rfkill->name = sc->rf_kill.rfkill_name;
+       sc->rf_kill.rfkill->data = sc;
+       sc->rf_kill.rfkill->toggle_radio = ath_sw_toggle_radio;
+       sc->rf_kill.rfkill->state = RFKILL_STATE_UNBLOCKED;
+       sc->rf_kill.rfkill->user_claim_unsupported = 1;
+
+       return 0;
+}
+
+/* Deinitialize rfkill */
+static void ath_deinit_rfkill(struct ath_softc *sc)
+{
+       if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
+               cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
+
+       if (sc->sc_flags & SC_OP_RFKILL_REGISTERED) {
+               rfkill_unregister(sc->rf_kill.rfkill);
+               sc->sc_flags &= ~SC_OP_RFKILL_REGISTERED;
+               sc->rf_kill.rfkill = NULL;
+       }
+}
+#endif /* CONFIG_RFKILL */
+
 static int ath_detach(struct ath_softc *sc)
 {
        struct ieee80211_hw *hw = sc->hw;
        /* Deinit LED control */
        ath_deinit_leds(sc);
 
+#ifdef CONFIG_RFKILL
+       /* deinit rfkill */
+       ath_deinit_rfkill(sc);
+#endif
+
        /* Unregister hw */
 
        ieee80211_unregister_hw(hw);
        /* Initialize LED control */
        ath_init_leds(sc);
 
+#ifdef CONFIG_RFKILL
+       /* Initialze h/w Rfkill */
+       if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
+               INIT_DELAYED_WORK(&sc->rf_kill.rfkill_poll, ath_rfkill_poll);
+
+       /* Initialize s/w rfkill */
+       if (ath_init_sw_rfkill(sc))
+               goto detach;
+#endif
+
        /* initialize tx/rx engine */
 
        error = ath_tx_init(sc, ATH_TXBUF);
                return error;
        }
 
+#ifdef CONFIG_RFKILL
+       /* Start rfkill polling */
+       if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
+               queue_delayed_work(sc->hw->workqueue,
+                                  &sc->rf_kill.rfkill_poll, 0);
+
+       if (!(sc->sc_flags & SC_OP_RFKILL_REGISTERED)) {
+               if (rfkill_register(sc->rf_kill.rfkill)) {
+                       DPRINTF(sc, ATH_DBG_FATAL,
+                                       "Unable to register rfkill\n");
+                       rfkill_free(sc->rf_kill.rfkill);
+
+                       /* Deinitialize the device */
+                       if (sc->pdev->irq)
+                               free_irq(sc->pdev->irq, sc);
+                       ath_detach(sc);
+                       pci_iounmap(sc->pdev, sc->mem);
+                       pci_release_region(sc->pdev, 0);
+                       pci_disable_device(sc->pdev);
+                       ieee80211_free_hw(hw);
+                       return -EIO;
+               } else {
+                       sc->sc_flags |= SC_OP_RFKILL_REGISTERED;
+               }
+       }
+#endif
+
        ieee80211_wake_queues(hw);
        return 0;
 }
                        "%s: Device is no longer present\n", __func__);
 
        ieee80211_stop_queues(hw);
+
+#ifdef CONFIG_RFKILL
+       if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
+               cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
+#endif
 }
 
 static int ath9k_add_interface(struct ieee80211_hw *hw,
        struct ath_softc *sc = hw->priv;
 
        ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
+
+#ifdef CONFIG_RFKILL
+       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);
                            AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
        ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
 
+#ifdef CONFIG_RFKILL
+       /*
+        * 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;
 }