move BT driver (rfkill) to net/rfkill/rfkill-rk.c
authorCMY <cmy@rock-chips.com>
Wed, 6 Jun 2012 13:37:38 +0000 (21:37 +0800)
committerCMY <cmy@rock-chips.com>
Wed, 6 Jun 2012 13:37:38 +0000 (21:37 +0800)
13 files changed:
arch/arm/configs/rk29_ddr3sdk_defconfig
arch/arm/mach-rk29/Makefile
arch/arm/mach-rk29/board-rk29-ddr3sdk.c
arch/arm/mach-rk29/devices.c
arch/arm/mach-rk30/Makefile
arch/arm/mach-rk30/board-rk30-sdk.c
arch/arm/mach-rk30/devices.c
drivers/bluetooth/hci_ldisc.c
include/linux/rfkill-rk.h [new file with mode: 0644]
net/rfkill/Kconfig
net/rfkill/Makefile
net/rfkill/core.c
net/rfkill/rfkill-rk.c [new file with mode: 0644]

index e0beb1bed7653f9f505a993c90f2f752f1da5f04..4f97bb59402440d3aea4d397f3ab09305687d91b 100755 (executable)
@@ -169,7 +169,9 @@ CONFIG_BT_BNEP=y
 CONFIG_BT_HIDP=y
 CONFIG_BT_HCIUART=y
 CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_LL=y
 CONFIG_BT_HCIBCM4325=y
+CONFIG_BT_AUTOSLEEP=y
 CONFIG_RFKILL=y
 # CONFIG_RFKILL_PM is not set
 CONFIG_DEVTMPFS=y
index 89009d8a521a2d52c4fad316906237bea9c9dc42..b41afc619a1b2227709c1a7df3ce553e277c620e 100755 (executable)
@@ -8,7 +8,7 @@ obj-$(CONFIG_RK29_I2C_INSRAM) += i2c_sram.o
 obj-y += spi_sram.o
 obj-$(CONFIG_RK29_PWM_INSRAM) += pwm_sram.o
 obj-$(CONFIG_MACH_RK29SDK) += board-rk29sdk.o board-rk29sdk-key.o board-rk29sdk-rfkill.o board-rk29sdk-power.o
-obj-$(CONFIG_MACH_RK29SDK_DDR3) += board-rk29-ddr3sdk.o board-rk29sdk-key.o board-rk29sdk-rfkill.o board-rk29sdk-power.o
+obj-$(CONFIG_MACH_RK29SDK_DDR3) += board-rk29-ddr3sdk.o board-rk29sdk-key.o board-rk29sdk-power.o
 obj-$(CONFIG_MACH_RK29_PHONESDK) += board-rk29-phonesdk.o board-rk29-phonesdk-key.o board-rk29-phonesdk-rfkill.o
 obj-$(CONFIG_MACH_RK29FIH) += board-rk29-fih.o board-rk29-fih-key.o board-rk29sdk-rfkill.o board-rk29sdk-power.o
 obj-$(CONFIG_MACH_RK29_A22) += board-rk29-a22.o board-rk29-a22-key.o board-rk29-a22-rfkill.o
index 5528ced18b1f1881946769509d0dfb65a5cd908e..fd8a01c80ab0caa70b1d744e92c9df50f0311e61 100755 (executable)
@@ -66,6 +66,9 @@
 #ifdef CONFIG_BU92747GUW_CIR
 #include "../../../drivers/cir/bu92747guw_cir.h"
 #endif
+#include <linux/rfkill-rk.h>
+
+
 #ifdef CONFIG_VIDEO_RK29
 /*---------------- Camera Sensor Macro Define Begin  ------------------------*/
 /*---------------- Camera Sensor Configuration Macro Begin ------------------------*/
@@ -2412,7 +2415,6 @@ int rk29sdk_bt_power_state = 0;
 #ifdef CONFIG_WIFI_CONTROL_FUNC
 #define RK29SDK_WIFI_BT_GPIO_POWER_N       RK29_PIN5_PD6
 #define RK29SDK_WIFI_GPIO_RESET_N          RK29_PIN6_PC0
-#define RK29SDK_BT_GPIO_RESET_N            RK29_PIN6_PC4
 
 static int rk29sdk_wifi_cd = 0;   /* wifi virtual 'card detect' status */
 static void (*wifi_status_cb)(int card_present, void *dev_id);
@@ -2445,15 +2447,8 @@ static int rk29sdk_wifi_bt_gpio_control_init(void)
            return -1;
     }
 
-    if (gpio_request(RK29SDK_BT_GPIO_RESET_N, "bt reset")) {
-          pr_info("%s: request bt reset gpio failed\n", __func__);
-          gpio_free(RK29SDK_WIFI_GPIO_RESET_N);
-          return -1;
-    }
-
     gpio_direction_output(RK29SDK_WIFI_BT_GPIO_POWER_N, GPIO_LOW);
     gpio_direction_output(RK29SDK_WIFI_GPIO_RESET_N,    GPIO_LOW);
-    gpio_direction_output(RK29SDK_BT_GPIO_RESET_N,      GPIO_LOW);
 
     #if defined(CONFIG_SDMMC1_RK29) && !defined(CONFIG_SDMMC_RK29_OLD)
     
@@ -2608,13 +2603,6 @@ static struct platform_device rk29sdk_wifi_device = {
 #endif
 
 
-/* bluetooth rfkill device */
-static struct platform_device rk29sdk_rfkill = {
-        .name = "rk29sdk_rfkill",
-        .id = -1,
-};
-
-
 //#ifdef CONFIG_VIVANTE
 #define GPU_HIGH_CLOCK        552
 #define GPU_LOW_CLOCK         (periph_pll_default / 1000000) /* same as general pll clock rate below */
@@ -2733,6 +2721,63 @@ static struct platform_device rk29_device_pwm_leds = {
 };
 
 #endif
+
+#ifdef CONFIG_BT
+// bluetooth rfkill device, its driver in net/rfkill/rfkill-rk.c
+static struct rfkill_rk_platform_data rfkill_rk_platdata = {
+    .type               = RFKILL_TYPE_BLUETOOTH,
+
+    .poweron_gpio       = { // BT_REG_ON
+        .io             = RK29_PIN5_PD6,
+        .enable         = GPIO_HIGH,
+        .iomux          = {
+            .name       = GPIO5D6_SDMMC1PWREN_NAME,
+            .fgpio      = GPIO5H_GPIO5D6,
+        },
+    },
+
+    .reset_gpio         = { // BT_RST
+        .io             = RK29_PIN6_PC4, // set io to INVALID_GPIO for disable it
+        .enable         = GPIO_LOW,
+        .iomux          = {
+            .name       = NULL,
+        },
+    },
+
+    .wake_gpio          = { // BT_WAKE, use to control bt's sleep and wakeup
+        .io             = RK29_PIN6_PC5, // set io to INVALID_GPIO for disable it
+        .enable         = GPIO_HIGH,
+        .iomux          = {
+            .name       = NULL,
+        },
+    },
+
+    .wake_host_irq      = { // BT_HOST_WAKE, for bt wakeup host when it is in deep sleep
+        .gpio           = {
+            .io         = INVALID_GPIO/*RK30_PIN6_PA2*/, // set io to INVALID_GPIO for disable it
+        },
+    },
+
+    .rts_gpio           = { // UART_RTS, enable or disable BT's data coming
+        .io             = RK29_PIN2_PA7, // set io to INVALID_GPIO for disable it
+        .enable         = GPIO_LOW,
+        .iomux          = {
+            .name       = GPIO2A7_UART2RTSN_NAME,
+            .fgpio      = GPIO2L_GPIO2A7,
+            .fmux       = GPIO2L_UART2_RTS_N,
+        },
+    },
+};
+
+struct platform_device device_rfkill_rk = {
+    .name   = "rfkill_rk",
+    .id     = -1,
+    .dev    = {
+        .platform_data = &rfkill_rk_platdata,
+    },
+};
+#endif
+
 static void __init rk29_board_iomux_init(void)
 {
        #ifdef CONFIG_RK29_PWM_REGULATOR
@@ -2815,10 +2860,6 @@ static struct platform_device *devices[] __initdata = {
         &rk29sdk_wifi_device,
 #endif
 
-#ifdef CONFIG_BT
-        &rk29sdk_rfkill,
-#endif
-
 #ifdef CONFIG_MTD_NAND_RK29
        &rk29_device_nand,
 #endif
index 779a9f13786c62db65b6d3a1aca4c6055cd80338..26cdb9cbd548008e519f8755e709211ca9946ecb 100644 (file)
@@ -884,6 +884,10 @@ static int __init rk29_init_devices(void)
 {
        platform_device_register(&rk29_device_pmu);
        platform_device_register(&rk29_device_pcm);
+#ifdef CONFIG_BT
+    extern struct platform_device device_rfkill_rk;
+    platform_device_register(&device_rfkill_rk);
+#endif
         return 0;
 }
 arch_initcall(rk29_init_devices);
index 0b585d46673953182b908c9f492f5fb428492cf9..90c0919034dbd264834ac8928474d46e8d9974b4 100755 (executable)
@@ -19,8 +19,8 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle.o
 obj-$(CONFIG_CPU_FREQ) += cpufreq.o
 obj-$(CONFIG_DVFS) += dvfs.o
 
-obj-$(CONFIG_MACH_RK30_SDK) += board-rk30-sdk.o board-rk30-sdk-key.o board-rk30-sdk-rfkill.o
 obj-$(CONFIG_MACH_RK3066_SDK) += board-rk30-sdk.o board-rk30-sdk-key.o board-rk30-sdk-rfkill.o
+obj-$(CONFIG_MACH_RK30_SDK) += board-rk30-sdk.o board-rk30-sdk-key.o
 obj-$(CONFIG_MACH_RK30_PHONE) += board-rk30-phone.o board-rk30-phone-key.o board-rk30-phone-rfkill.o
 obj-$(CONFIG_MACH_RK30_PHONE_LOQUAT) += board-rk30-phone-loquat.o board-rk30-phone-loquat-key.o board-rk30-phone-loquat-rfkill.o
 
index 94bac7a6e2a8aac7504d95ea71668dec0c67ed85..8a484c8adca61fdaad804f9f85ac0dfa2992a0d4 100755 (executable)
@@ -42,6 +42,8 @@
 #include <mach/iomux.h>
 #include <linux/fb.h>
 #include <linux/regulator/machine.h>
+#include <linux/rfkill-rk.h>
+
 #if defined(CONFIG_HDMI_RK30)
        #include "../../../drivers/video/rockchip/hdmi/rk_hdmi.h"
 #endif
@@ -1195,12 +1197,6 @@ struct rk29_sdmmc_platform_data default_sdmmc1_data = {
  * the end of setting for SDMMC devices
 **************************************************************************************************/
 
-/* bluetooth rfkill device */
-static struct platform_device rk29sdk_rfkill = {
-        .name = "rk29sdk_rfkill",
-        .id = -1,
-};
-
 #ifdef CONFIG_BATTERY_RK30_ADC
 static struct rk30_adc_battery_platform_data rk30_adc_battery_platdata = {
         .dc_det_pin      = RK30_PIN6_PA5,
@@ -1247,9 +1243,6 @@ static struct platform_device *devices[] __initdata = {
 #ifdef CONFIG_WIFI_CONTROL_FUNC
        &rk29sdk_wifi_device,
 #endif
-#ifdef CONFIG_BT
-       &rk29sdk_rfkill,
-#endif
 #ifdef CONFIG_RK29_SUPPORT_MODEM
        &rk30_device_modem,
 #endif
@@ -1422,6 +1415,68 @@ static struct i2c_board_info __initdata i2c_gpio_info[] = {
 };
 #endif
 
+#ifdef CONFIG_BT
+// bluetooth rfkill device, its driver in net/rfkill/rfkill-rk.c
+static struct rfkill_rk_platform_data rfkill_rk_platdata = {
+    .type               = RFKILL_TYPE_BLUETOOTH,
+
+    .poweron_gpio       = { // BT_REG_ON
+        .io             = RK30_PIN3_PC7,
+        .enable         = GPIO_HIGH,
+        .iomux          = {
+            .name       = GPIO3C7_SDMMC1WRITEPRT_NAME,
+            .fgpio      = GPIO3C_GPIO3C7,
+        },
+    },
+
+    .reset_gpio         = { // BT_RST
+        .io             = RK30_PIN3_PD1, // set io to INVALID_GPIO for disable it
+        .enable         = GPIO_LOW,
+        .iomux          = {
+            .name       = GPIO3D1_SDMMC1BACKENDPWR_NAME,
+            .fgpio      = GPIO3D_GPIO3D1,
+        },
+    },
+
+    .wake_gpio          = { // BT_WAKE, use to control bt's sleep and wakeup
+        .io             = RK30_PIN3_PC6, // set io to INVALID_GPIO for disable it
+        .enable         = GPIO_HIGH,
+        .iomux          = {
+            .name       = GPIO3C6_SDMMC1DETECTN_NAME,
+            .fgpio      = GPIO3C_GPIO3C6,
+        },
+    },
+
+    .wake_host_irq      = { // BT_HOST_WAKE, for bt wakeup host when it is in deep sleep
+        .gpio           = {
+            .io         = RK30_PIN6_PA7, // set io to INVALID_GPIO for disable it
+            .iomux      = {
+                .name   = NULL,
+            },
+        },
+        .is_falling     = 1, // trigger type, set 1 for falling, set 0 for rising
+    },
+
+    .rts_gpio           = { // UART_RTS, enable or disable BT's data coming
+        .io             = RK30_PIN1_PA3, // set io to INVALID_GPIO for disable it
+        .enable         = GPIO_LOW,
+        .iomux          = {
+            .name       = GPIO1A3_UART0RTSN_NAME,
+            .fgpio      = GPIO1A_GPIO1A3,
+            .fmux       = GPIO1A_UART0_RTS_N,
+        },
+    },
+};
+
+struct platform_device device_rfkill_rk = {
+    .name   = "rfkill_rk",
+    .id     = -1,
+    .dev    = {
+        .platform_data = &rfkill_rk_platdata,
+    },
+};
+#endif
+
 static void __init rk30_i2c_register_board_info(void)
 {
 #ifdef CONFIG_I2C0_RK30
index 852ded9a79aa5dea14d2782eea3e2a4fd1bac081..35ecb998482146a98b0be2b2d95fc8f5d3176b50 100755 (executable)
@@ -28,6 +28,7 @@
 #include <mach/gpio.h>
 #include <mach/iomux.h>
 #include <plat/rk_fiq_debugger.h>
+#include <linux/rfkill-rk.h>
 
 #ifdef CONFIG_ADC_RK30
 static struct resource rk30_adc_resource[] = {
@@ -1170,6 +1171,10 @@ static int __init rk30_init_devices(void)
 #ifdef CONFIG_RK29_VMAC
        platform_device_register(&device_vmac);
 #endif
+#ifdef CONFIG_BT
+    extern struct platform_device device_rfkill_rk;
+    platform_device_register(&device_rfkill_rk);
+#endif
 
        return 0;
 }
index 18df94586dbdf0ca42ffc49621be5d0d9b01f54f..8a5b66980c65f7b486174f1bc15f0ac1fe92bc21 100755 (executable)
@@ -47,7 +47,7 @@
 #include "hci_uart.h"
 
 #ifdef CONFIG_BT_AUTOSLEEP
-extern int bcm4325_sleep(int bSleep);
+extern int rfkill_rk_sleep_bt(bool bSleep);
 #endif
 
 #define VERSION "2.2"
@@ -139,7 +139,7 @@ restart:
        clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
 /*added by Barry,for broadcom 4325*/
 #ifdef CONFIG_BT_AUTOSLEEP  
-       bcm4325_sleep(0);
+       rfkill_rk_sleep_bt(false);
 #endif
        while ((skb = hci_uart_dequeue(hu))) {
                int len;
diff --git a/include/linux/rfkill-rk.h b/include/linux/rfkill-rk.h
new file mode 100644 (file)
index 0000000..cb78dc0
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+
+#ifndef __RFKILL_GPIO_H
+#define __RFKILL_GPIO_H
+
+#include <linux/types.h>
+#include <linux/rfkill.h>
+
+#define RFKILL_RK_GPIO_NAME_SIZE   64
+
+struct rfkill_rk_iomux {
+    char    *name;
+    int     fgpio;
+    int     fmux;
+};
+
+struct rfkill_rk_gpio {
+    int     io;
+    char    name[RFKILL_RK_GPIO_NAME_SIZE];
+    int     enable; // disable = !enable
+    struct rfkill_rk_iomux  iomux;
+};
+
+struct rfkill_rk_irq {
+    char                    name[RFKILL_RK_GPIO_NAME_SIZE];
+    int                     is_falling;
+    struct rfkill_rk_gpio   gpio;
+    int                     irq;
+};
+
+/**
+ * struct rfkill_rk_platform_data - platform data for rfkill gpio device.
+ * for unused gpio's, the expected value is -1.
+ * @name:               name for the gpio rf kill instance
+ * @reset_gpio:         GPIO which is used for reseting rfkill switch
+ * @shutdown_gpio:      GPIO which is used for shutdown of rfkill switch
+ */
+
+struct rfkill_rk_platform_data {
+    char                    *name;
+    enum rfkill_type        type;
+    struct rfkill_rk_gpio   poweron_gpio;
+    struct rfkill_rk_gpio   reset_gpio;
+    struct rfkill_rk_gpio   wake_gpio;      // Host wake or sleep BT
+    struct rfkill_rk_irq    wake_host_irq;  // BT wakeup host
+    struct rfkill_rk_gpio   rts_gpio;
+};
+
+#endif /* __RFKILL_GPIO_H */
+
index 73111a5867ab79eba5be7bfd6c2bdf5500886b08..224786574160d1235ad3d38a9dd32fc5dea9e523 100644 (file)
@@ -53,3 +53,10 @@ config RFKILL_RESET
         depends on RFKILL && PM
         default n
 
+config RFKILL_RK
+        tristate "Rockchips RFKILL driver"
+        depends on RFKILL
+        default y
+        help
+          rockchips rfkill driver for rk29/rk30
+
index 311768783f4a116843349687c99a3cf65f6c6314..186dbab8b90bc1691c5ae391ecddd1cd0dce1986 100644 (file)
@@ -7,3 +7,4 @@ rfkill-$(CONFIG_RFKILL_INPUT)   += input.o
 obj-$(CONFIG_RFKILL)           += rfkill.o
 obj-$(CONFIG_RFKILL_REGULATOR) += rfkill-regulator.o
 obj-$(CONFIG_RFKILL_GPIO)      += rfkill-gpio.o
+obj-$(CONFIG_RFKILL_RK)                += rfkill-rk.o
index cfd0c89edf680bfacbad0f98084168c42a9d5711..df2dae6b27236432c3f8a5ff2a4a7dbf5f8c1d2b 100644 (file)
@@ -252,11 +252,7 @@ static bool __rfkill_set_hw_state(struct rfkill *rfkill,
  * Calls the set_block method (when applicable) and handles notifications
  * etc. as well.
  */
-#ifdef CONFIG_RFKILL_RESET
-void rfkill_set_block(struct rfkill *rfkill, bool blocked)
-#else
 static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
-#endif
 {
        unsigned long flags;
        int err;
@@ -307,9 +303,6 @@ static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
        rfkill_led_trigger_event(rfkill);
        rfkill_event(rfkill);
 }
-#ifdef CONFIG_RFKILL_RESET
-EXPORT_SYMBOL(rfkill_set_block);
-#endif
 
 #ifdef CONFIG_RFKILL_INPUT
 static atomic_t rfkill_input_disabled = ATOMIC_INIT(0);
diff --git a/net/rfkill/rfkill-rk.c b/net/rfkill/rfkill-rk.c
new file mode 100644 (file)
index 0000000..7c81864
--- /dev/null
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2012 ROCKCHIP, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/* Rock-chips rfkill driver for bluetooth
+ *
+ * BT Ô´ÂëÓÉÔ­À´µÄarch/arm/mach-rkxx/board-xxx-rfkill.cÒÆÖÁ´Ë´¦
+ * GPIOµÄÅäÖÃÔÚ arch/arm/mach-rkxx/board-rkxx-sdk.c µÄrfkill_rk_platdata½á¹¹ÌåÖÐ
+ *
+ * Çë¸ù¾Ýʵ¼ÊÇé¿öÅäÖúÃÀ¶ÑÀµÄ¸÷¸öGPIOÒý½Å£¬Òý½ÅÅäÖÃ˵Ã÷ÈçÏÂ:
+    .xxxx_gpio   = {
+      .io         = -1,      // GPIOÖµ£¬ -1 ±íʾÎ޴˹¦ÄÜ
+      .enable     = -1,      // Ê¹ÄÜ, GPIO_HIGH - ¸ßʹÄÜ£¬ GPIO_LOW - µÍʹÄÜ
+      .iomux      = {
+          .name    = NULL,   // IOMUXÃû³Æ£¬NULL ±íʾËüÊǵ¥¹¦ÄÜ£¬²»ÐèÒªmux
+          .fgpio   = -1,     // ÅäÖÃΪGPIOʱ£¬Ó¦ÉèÖõÄÖµ
+          .fmux   = -1,      // ÅäÖÃΪ¸´Óù¦ÄÜʱ£¬Ó¦ÉèÖõÄÖµ
+      },
+    },
+*/
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/rfkill.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <asm/gpio.h>
+#include <mach/iomux.h>
+#include <linux/delay.h>
+#include <linux/rfkill-rk.h>
+#include <linux/wakelock.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <linux/suspend.h>
+
+#if 0
+#define DBG(x...)   printk(KERN_INFO "[BT_RFKILL]: "x)
+#else
+#define DBG(x...)
+#endif
+
+#define LOG(x...)   printk(KERN_INFO "[BT_RFKILL]: "x)
+
+#define BT_WAKEUP_TIMEOUT           3000
+#define BT_IRQ_WAKELOCK_TIMEOUT     10*1000
+
+#define BT_BLOCKED     true
+#define BT_UNBLOCK     false
+#define BT_SLEEP       true
+#define BT_WAKEUP      false
+
+enum {
+    IOMUX_FNORMAL=0,
+    IOMUX_FGPIO,
+    IOMUX_FMUX,
+};
+
+#ifdef CONFIG_ARCH_RK29
+    #define rk_mux_api_set(name,mode)      rk29_mux_api_set(name,mode)
+#elif defined (CONFIG_ARCH_RK30)
+    #define rk_mux_api_set(name,mode)      rk30_mux_api_set(name,mode)
+#else
+    #define rk_mux_api_set(name,mode)
+#endif
+
+// RK29+BCM4329, ÆäwifiÓëbtµÄpower¿ØÖƽÅÊǽÓÔÚÒ»ÆðµÄ
+// ÔÚ¸øbtϵçʱ£¬ÐèÒªÏÈÅжÏwifi״̬
+#if defined(CONFIG_ARCH_RK29) && defined(CONFIG_BCM4329)
+#define WIFI_BT_POWER_TOGGLE    1
+extern int rk29sdk_bt_power_state;
+extern int rk29sdk_wifi_power_state;
+#else
+#define WIFI_BT_POWER_TOGGLE    0
+#endif
+
+struct rfkill_rk_data {
+    // Ö¸ÏòboardÖж¨ÒåµÄplatform data
+       struct rfkill_rk_platform_data  *pdata;
+
+    // Ö¸ÏòrfkillÉ豸£¬ËüÓÉrfkill_alloc´´½¨
+       struct rfkill                           *rfkill_dev;
+
+    // ÔÚIRQÖжϺ¯ÊýÖÐʹÓã¬È·±£ÔÚBT»½ÐÑAPºó£¬²»»áÁ¢¼´
+    // µ÷ÓÃsuspend½øÈë˯Ãß
+    struct wake_lock            bt_irq_wl;
+    
+    // ÔÚ»½ÐÑBTºó£¬Í¨¹ý¸Ãdelay workÑÓʱһ¶Îʱ¼äºó½øÈë˯Ãß
+    // Èç¹ûdelay work»¹Î´Ö´ÐоÍÓÖÀ´Ò»´Î BT wake£¬Ôò»áÏÈcancel
+    // ÒÑÓеĠdelay work£¬²¢ÖØмÆʱ¡£
+    struct delayed_work         bt_sleep_delay_work;
+};
+
+struct rfkill_rk_data *g_rfkill = NULL;
+
+static const char bt_name[] = 
+#if defined(CONFIG_RKWIFI)
+    #if defined(CONFIG_RKWIFI_26M)
+        "rk903_26M"
+    #else
+        "rk903"
+    #endif
+#elif defined(CONFIG_BCM4329)
+        "bcm4329"
+#elif defined(CONFIG_MV8787)
+        "mv8787"
+#else
+        "bt_default"
+#endif
+;
+
+/*
+ *  rfkill_rk_wake_host_irq - BT_WAKE_HOST Öжϻص÷º¯Êý
+ *      ÉêÇëÒ»¸öwakelock£¬ÒÔÈ·±£´Ë´Î»½ÐѲ»»áÂíÉϽáÊø
+ */
+static irqreturn_t rfkill_rk_wake_host_irq(int irq, void *dev)
+{
+    struct rfkill_rk_data *rfkill = dev;
+    LOG("BT_WAKE_HOST IRQ fired\n");
+    
+    DBG("BT IRQ wakeup, request %dms wakelock\n", BT_IRQ_WAKELOCK_TIMEOUT);
+
+    wake_lock_timeout(&rfkill->bt_irq_wl, 
+                    msecs_to_jiffies(BT_IRQ_WAKELOCK_TIMEOUT));
+    
+       return IRQ_HANDLED;
+}
+
+/*
+ * rfkill_rk_setup_gpio - ÉèÖÃGPIO
+ *      gpio    - ÒªÉèÖõÄGPIO
+ *      mux     - iomux Ê²Ã´¹¦ÄÜ
+ *      prefix,name - ×é³É¸ÃIOµÄÃû³Æ
+ * ·µ»ØÖµ
+ *      ·µ»ØÖµÓë gpio_request Ïàͬ
+ */
+static int rfkill_rk_setup_gpio(struct rfkill_rk_gpio* gpio, int mux, const char* prefix, const char* name)
+{
+       if (gpio_is_valid(gpio->io)) {
+        int ret=0;
+        sprintf(gpio->name, "%s_%s", prefix, name);
+               ret = gpio_request(gpio->io, gpio->name);
+               if (ret) {
+                       LOG("Failed to get %s gpio.\n", gpio->name);
+                       return -1;
+               }
+        if (gpio->iomux.name)
+        {
+            if (mux==1)
+                rk_mux_api_set(gpio->iomux.name, gpio->iomux.fgpio);
+            else if (mux==2)
+                rk_mux_api_set(gpio->iomux.name, gpio->iomux.fmux);
+            else
+                ;// do nothing
+        }
+       }
+
+    return 0;
+}
+
+// ÉèÖàBT_WAKE_HOST IRQ
+static int rfkill_rk_setup_wake_irq(struct rfkill_rk_data* rfkill)
+{
+    int ret=0;
+    struct rfkill_rk_irq* irq = &(rfkill->pdata->wake_host_irq);
+    
+    ret = rfkill_rk_setup_gpio(&irq->gpio, IOMUX_FGPIO, rfkill->pdata->name, "wake_host");
+    if (ret) goto fail1;
+
+    if (gpio_is_valid(irq->gpio.io))
+    {
+        ret = gpio_pull_updown(irq->gpio.io, irq->is_falling?GPIOPullUp:GPIOPullDown);
+        if (ret) goto fail2;
+        DBG("Request irq for bt wakeup host\n");
+        irq->irq = gpio_to_irq(irq->gpio.io);
+        sprintf(irq->name, "%s_irq", irq->gpio.name);
+        ret = request_irq(irq->irq,
+                    rfkill_rk_wake_host_irq,
+                    irq->is_falling?IRQF_TRIGGER_FALLING:IRQF_TRIGGER_RISING,
+                    irq->name,
+                    rfkill);
+        if (ret) goto fail2;
+        disable_irq(irq->irq);
+        ret = enable_irq_wake(irq->irq);
+        if (ret) goto fail3;
+    }
+
+    return ret;
+
+fail3:
+    free_irq(irq->gpio.io, rfkill);
+fail2:
+    gpio_free(irq->gpio.io);
+fail1:
+    return ret;
+}
+
+static inline void rfkill_rk_sleep_bt_internal(struct rfkill_rk_data *rfkill, bool sleep)
+{
+    struct rfkill_rk_gpio *wake = &rfkill->pdata->wake_gpio;
+    
+    DBG("*** bt sleep: %d ***\n", sleep);
+    gpio_direction_output(wake->io, sleep?!wake->enable:wake->enable);
+}
+
+static void rfkill_rk_delay_sleep_bt(struct work_struct *work)
+{
+    struct rfkill_rk_data *rfkill = NULL;
+    DBG("Enter %s\n",__FUNCTION__);
+
+    rfkill = container_of(work, struct rfkill_rk_data, bt_sleep_delay_work.work);
+
+    rfkill_rk_sleep_bt_internal(rfkill, BT_SLEEP);
+}
+
+/*
+ *  rfkill_rk_sleep_bt - Sleep or Wakeup BT
+ *      1 ÔÚ¸øBTÉϵçʱºò£¬µ÷Óøú¯Êý»½ÐÑBT
+ *      2 ÔÚsuspendÇý¶¯Ê±ºò£¬µ÷Óøú¯Êý˯ÃßBT
+ *      3 ÔÚHCIÇý¶¯ÖУ¬µ±ÓÐÃüÁî·¢Ë͸øBTʱ£¬µ÷Óøú¯Êý»½ÐÑBT
+ *
+ *  ¶ÔÀ¶ÑÀµÄÖ¸Áî·¢ËÍÊÇ·¢ÉúÔÚÀ¶ÑÀÉϵçÖ®ºó£¬Òò´Ë1Óë3²»»áͬʱµ÷Óøú¯Êý
+ *  À¶ÑÀÖ¸Áî·¢ËÍÓÉÓû§²ã·¢Æð£¬2Óë3²»»áͬʱµ÷Óøú¯Êý
+ */
+void rfkill_rk_sleep_bt(bool sleep)
+{
+    struct rfkill_rk_data *rfkill = g_rfkill;
+    struct rfkill_rk_gpio *wake;
+    bool ret;
+    DBG("Enter %s\n",__FUNCTION__);
+    
+    if (rfkill==NULL)
+    {
+        LOG("*** RFKILL is empty???\n");
+        return;
+    }
+
+    wake = &rfkill->pdata->wake_gpio;
+    if (!gpio_is_valid(wake->io))
+    {
+        DBG("*** Not support bt wakeup and sleep\n");
+        return;
+    }
+
+    // È¡Ïûdelay work£¬Èç¹ûwork´¦ÓÚpendding£¬ÔòÁ¢¼´È¡Ïû
+    // Èç¹ûwork´¦ÓÚrunning£¬ÔòµÈ´ýÖ±µ½¸Ãwork½áÊø
+    ret = cancel_delayed_work_sync(&rfkill->bt_sleep_delay_work);
+
+    rfkill_rk_sleep_bt_internal(rfkill, sleep);
+
+    if (sleep==BT_WAKEUP)
+    {
+        // ÖØÐÂÉèÖÃdelay work
+        schedule_delayed_work(&rfkill->bt_sleep_delay_work, 
+                            msecs_to_jiffies(BT_WAKEUP_TIMEOUT));
+    }
+}
+EXPORT_SYMBOL(rfkill_rk_sleep_bt);
+
+static int rfkill_rk_set_power(void *data, bool blocked)
+{
+       struct rfkill_rk_data *rfkill = data;
+    struct rfkill_rk_gpio *poweron = &rfkill->pdata->poweron_gpio;
+    struct rfkill_rk_gpio *reset = &rfkill->pdata->reset_gpio;
+
+    DBG("Enter %s\n", __func__);
+
+    DBG("Set blocked:%d\n", blocked);
+
+       if (false == blocked) { 
+        rfkill_rk_sleep_bt(BT_WAKEUP); // ensure bt is wakeup
+        
+               if (gpio_is_valid(poweron->io))
+        {
+                       gpio_direction_output(poweron->io, poweron->enable);
+            msleep(20);
+        }
+               if (gpio_is_valid(reset->io))
+        {
+                       gpio_direction_output(reset->io, reset->enable);
+            msleep(20);
+                       gpio_direction_output(reset->io, !reset->enable);
+            msleep(20);
+        }
+
+       LOG("bt turn on power\n");
+       } else {
+#if WIFI_BT_POWER_TOGGLE
+               if (!rk29sdk_wifi_power_state) {
+#endif
+            if (gpio_is_valid(poweron->io))
+            {      
+                gpio_direction_output(poweron->io, !poweron->enable);
+                msleep(20);
+            }
+
+               LOG("bt shut off power\n");
+#if WIFI_BT_POWER_TOGGLE
+               }else {
+                       LOG("bt shouldn't shut off power, wifi is using it!\n");
+               }
+#endif
+               if (gpio_is_valid(reset->io))
+        {      
+                       gpio_direction_output(reset->io, reset->enable);/* bt reset active*/
+            msleep(20);
+        }
+       }
+
+#if WIFI_BT_POWER_TOGGLE
+       rk29sdk_bt_power_state = !blocked;
+#endif
+       return 0;
+}
+
+static int rfkill_rk_suspend(struct platform_device *pdev, pm_message_t state)
+{
+    struct rfkill_rk_data *rfkill = platform_get_drvdata(pdev);
+    struct rfkill_rk_gpio* rts = &rfkill->pdata->rts_gpio;
+    struct rfkill_rk_irq*  wake_host_irq = &rfkill->pdata->wake_host_irq;
+    DBG("Enter %s\n",__FUNCTION__);
+
+    //To prevent uart to receive bt data when suspended
+    if (gpio_is_valid(rts->io))
+    {
+        DBG("Disable UART_RTS\n");
+        if (rts->iomux.name)
+        {
+            rk_mux_api_set(rts->iomux.name, rts->iomux.fgpio);
+        }
+        gpio_direction_output(rts->io, !rts->enable);
+    }
+
+#ifdef CONFIG_BT_AUTOSLEEP
+    // BT½øÈë˯Ãß״̬£¬²»½ÓÊÕÖ÷¿ØÊý¾Ý
+    rfkill_rk_sleep_bt(BT_SLEEP);
+#endif
+
+    /* ÖÁ´Ë£¬À¶ÑÀ²»ÔÙËÍÊý¾Ýµ½UART£¬Ò²²»ÔÙ½ÓÊÕÖ÷¿ØµÄUARTÊý¾Ý
+     * ½Ó×ŵ÷ÓÃenable_irqʹÄÜ bt_wake_host irq£¬µ±Ô¶¶ËÉ豸ÓÐÊý¾Ý
+     * µ½À´Ê±£¬½«Í¨¹ý¸ÃIRQ»½ÐÑÖ÷¿Ø
+     */
+
+    // enable bt wakeup host
+    if (gpio_is_valid(wake_host_irq->gpio.io))
+    {
+        DBG("enable irq for bt wakeup host\n");
+        enable_irq(wake_host_irq->irq);
+    }
+
+#ifdef CONFIG_RFKILL_RESET
+    rfkill_set_states(rfkill->rfkill_dev, BT_BLOCKED, false);
+    rfkill_rk_set_power(rfkill, BT_BLOCKED);
+#endif
+
+    return 0;
+}
+
+static int rfkill_rk_resume(struct platform_device *pdev)
+{
+    struct rfkill_rk_data *rfkill = platform_get_drvdata(pdev);
+    struct rfkill_rk_irq*  wake_host_irq = NULL;
+    struct rfkill_rk_gpio* rts = NULL;
+    DBG("Enter %s\n",__FUNCTION__);
+
+    wake_host_irq = &rfkill->pdata->wake_host_irq;
+    rts = &rfkill->pdata->rts_gpio;
+
+    if (gpio_is_valid(wake_host_irq->gpio.io))
+    {
+        // ½ûÓõô BT_WAKE_HOST IRQ£¬È·±£ÔÚϵͳ»½ÐѺ󲻻áÒòBTµÄ²Ù×÷
+        // ¶ø¶à´Î´¥·¢¸ÃÖжÏ
+        DBG("** disable bt wakeup host\n");
+        disable_irq(wake_host_irq->irq);
+    }
+
+    /* Ê¹ÓÃUART_RTS£¬´ËʱÀ¶ÑÀÈç¹ûÓÐÊý¾Ý¾Í»áË͵½UART
+     * Éϲã·ÖÎöËÍÀ´µÄÊý¾Ý²¢×ö³öÏàÓ¦µÄ¶¯×÷£¬±ÈÈç: ËÍÀ´µÄÊÇ
+     * Åä¶ÔÇëÇó£¬ÔòÉϲ㽫»áÁÁÆÁ²¢µ¯³öÅä¶Ô½çÃæ
+     */
+    if (gpio_is_valid(rts->io))
+    {
+        DBG("Enable UART_RTS\n");
+        gpio_direction_output(rts->io, rts->enable);
+        if (rts->iomux.name)
+        {
+            rk_mux_api_set(rts->iomux.name, rts->iomux.fmux);
+        }
+    }
+
+    return 0;
+}
+
+static const struct rfkill_ops rfkill_rk_ops = {
+    .set_block = rfkill_rk_set_power,
+};
+
+static int rfkill_rk_probe(struct platform_device *pdev)
+{
+       struct rfkill_rk_data *rfkill;
+       struct rfkill_rk_platform_data *pdata = pdev->dev.platform_data;
+       int ret = 0;
+
+    DBG("Enter %s\n", __func__);
+
+       if (!pdata) {
+               LOG("%s: No platform data specified\n", __func__);
+               return -EINVAL;
+       }
+
+    pdata->name = (char*)bt_name;
+
+       rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL);
+       if (!rfkill)
+               return -ENOMEM;
+
+       rfkill->pdata = pdata;
+    g_rfkill = rfkill;
+
+    // ÉêÇëGPIOÒÔ¼°IRQ
+    DBG("init gpio\n");
+    // ¶ÔÓÚRK29 BCM4329£¬ËüµÄpoweron ioÓëwifi¹²Óã¬ÔÚboadÎļþÖÐÒѾ­request
+    // ´Ë´¦²»ÓÃÈ¥ÉêÇë
+#if !WIFI_BT_POWER_TOGGLE
+    ret = rfkill_rk_setup_gpio(&pdata->poweron_gpio, IOMUX_FGPIO, pdata->name, "poweron");
+    if (ret) goto fail_alloc;
+#endif
+
+    ret = rfkill_rk_setup_gpio(&pdata->reset_gpio, IOMUX_FGPIO, pdata->name, "reset");
+    if (ret) goto fail_poweron;
+
+    ret = rfkill_rk_setup_gpio(&pdata->wake_gpio, IOMUX_FGPIO, pdata->name, "wake");
+    if (ret) goto fail_reset;
+
+    ret = rfkill_rk_setup_wake_irq(rfkill);
+    if (ret) goto fail_wake;
+
+    ret = rfkill_rk_setup_gpio(&(pdata->rts_gpio), IOMUX_FNORMAL, rfkill->pdata->name, "rts");
+    if (ret) goto fail_wake_host_irq;
+
+    // ´´½¨²¢×¢²áRFKILLÉ豸
+    DBG("setup rfkill\n");
+       rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type,
+                               &rfkill_rk_ops, rfkill);
+       if (!rfkill->rfkill_dev)
+               goto fail_rts;
+
+    // cmy: ÉèÖÃrfkill³õʼ״̬Ϊblocked£¬ÔÚ×¢²áʱ²»»áµ÷Óàset_blockedº¯Êý
+    rfkill_set_states(rfkill->rfkill_dev, BT_BLOCKED, false);
+       ret = rfkill_register(rfkill->rfkill_dev);
+       if (ret < 0)
+               goto fail_rfkill;
+
+    wake_lock_init(&(rfkill->bt_irq_wl), WAKE_LOCK_SUSPEND, "rfkill_rk_irq_wl");
+    INIT_DELAYED_WORK(&rfkill->bt_sleep_delay_work, rfkill_rk_delay_sleep_bt);
+
+    // cmy: ÉèÖÃÀ¶ÑÀµçÔ´µÄ״̬Ϊ blocked
+    rfkill_rk_set_power(rfkill, BT_BLOCKED);
+
+       platform_set_drvdata(pdev, rfkill);
+
+    LOG("%s device registered.\n", pdata->name);
+
+       return 0;
+
+fail_rfkill:
+       rfkill_destroy(rfkill->rfkill_dev);
+fail_rts:
+    if (gpio_is_valid(pdata->rts_gpio.io))
+        gpio_free(pdata->rts_gpio.io);
+fail_wake_host_irq:
+    if (gpio_is_valid(pdata->wake_host_irq.gpio.io)){
+        free_irq(pdata->wake_host_irq.irq, rfkill);
+        gpio_free(pdata->wake_host_irq.gpio.io);
+    }
+fail_wake:
+    if (gpio_is_valid(pdata->wake_gpio.io))
+        gpio_free(pdata->wake_gpio.io);
+fail_reset:
+       if (gpio_is_valid(pdata->reset_gpio.io))
+               gpio_free(pdata->reset_gpio.io);
+fail_poweron:
+#if !WIFI_BT_POWER_TOGGLE
+    if (gpio_is_valid(pdata->poweron_gpio.io))
+        gpio_free(pdata->poweron_gpio.io);
+#endif
+fail_alloc:
+       kfree(rfkill);
+    g_rfkill = NULL;
+
+       return ret;
+}
+
+static int rfkill_rk_remove(struct platform_device *pdev)
+{
+       struct rfkill_rk_data *rfkill = platform_get_drvdata(pdev);
+
+    LOG("Enter %s\n", __func__);
+
+       rfkill_unregister(rfkill->rfkill_dev);
+       rfkill_destroy(rfkill->rfkill_dev);
+
+    wake_lock_destroy(&rfkill->bt_irq_wl);
+    
+    cancel_delayed_work_sync(&rfkill->bt_sleep_delay_work);
+
+    // free gpio
+    if (gpio_is_valid(rfkill->pdata->rts_gpio.io))
+        gpio_free(rfkill->pdata->rts_gpio.io);
+    
+    if (gpio_is_valid(rfkill->pdata->wake_host_irq.gpio.io)){
+        free_irq(rfkill->pdata->wake_host_irq.irq, rfkill);
+        gpio_free(rfkill->pdata->wake_host_irq.gpio.io);
+    }
+    
+    if (gpio_is_valid(rfkill->pdata->wake_gpio.io))
+        gpio_free(rfkill->pdata->wake_gpio.io);
+    
+    if (gpio_is_valid(rfkill->pdata->reset_gpio.io))
+        gpio_free(rfkill->pdata->reset_gpio.io);
+    
+    if (gpio_is_valid(rfkill->pdata->poweron_gpio.io))
+        gpio_free(rfkill->pdata->poweron_gpio.io);
+
+    kfree(rfkill);
+    g_rfkill = NULL;
+
+       return 0;
+}
+
+static struct platform_driver rfkill_rk_driver = {
+       .probe = rfkill_rk_probe,
+       .remove = __devexit_p(rfkill_rk_remove),
+    .suspend = rfkill_rk_suspend,
+    .resume = rfkill_rk_resume,
+       .driver = {
+        .name = "rfkill_rk",
+        .owner = THIS_MODULE,
+       },
+};
+
+static int __init rfkill_rk_init(void)
+{
+    LOG("Enter %s\n", __func__);
+       return platform_driver_register(&rfkill_rk_driver);
+}
+
+static void __exit rfkill_rk_exit(void)
+{
+    LOG("Enter %s\n", __func__);
+       platform_driver_unregister(&rfkill_rk_driver);
+}
+
+module_init(rfkill_rk_init);
+module_exit(rfkill_rk_exit);
+
+MODULE_DESCRIPTION("rock-chips rfkill for Bluetooth v0.2");
+MODULE_AUTHOR("cmy@rock-chips.com, cz@rock-chips.com");
+MODULE_LICENSE("GPL");
+