From 8d66546279459beed6b49ce5a5fd30f3a35baa1b Mon Sep 17 00:00:00 2001 From: CMY Date: Wed, 6 Jun 2012 21:37:38 +0800 Subject: [PATCH] move BT driver (rfkill) to net/rfkill/rfkill-rk.c --- arch/arm/configs/rk29_ddr3sdk_defconfig | 2 + arch/arm/mach-rk29/Makefile | 2 +- arch/arm/mach-rk29/board-rk29-ddr3sdk.c | 79 +++- arch/arm/mach-rk29/devices.c | 4 + arch/arm/mach-rk30/Makefile | 2 +- arch/arm/mach-rk30/board-rk30-sdk.c | 73 ++- arch/arm/mach-rk30/devices.c | 5 + drivers/bluetooth/hci_ldisc.c | 4 +- include/linux/rfkill-rk.h | 67 +++ net/rfkill/Kconfig | 7 + net/rfkill/Makefile | 1 + net/rfkill/core.c | 7 - net/rfkill/rfkill-rk.c | 569 ++++++++++++++++++++++++ 13 files changed, 783 insertions(+), 39 deletions(-) create mode 100644 include/linux/rfkill-rk.h create mode 100644 net/rfkill/rfkill-rk.c diff --git a/arch/arm/configs/rk29_ddr3sdk_defconfig b/arch/arm/configs/rk29_ddr3sdk_defconfig index e0beb1bed765..4f97bb594024 100755 --- a/arch/arm/configs/rk29_ddr3sdk_defconfig +++ b/arch/arm/configs/rk29_ddr3sdk_defconfig @@ -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 diff --git a/arch/arm/mach-rk29/Makefile b/arch/arm/mach-rk29/Makefile index 89009d8a521a..b41afc619a1b 100755 --- a/arch/arm/mach-rk29/Makefile +++ b/arch/arm/mach-rk29/Makefile @@ -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 diff --git a/arch/arm/mach-rk29/board-rk29-ddr3sdk.c b/arch/arm/mach-rk29/board-rk29-ddr3sdk.c index 5528ced18b1f..fd8a01c80ab0 100755 --- a/arch/arm/mach-rk29/board-rk29-ddr3sdk.c +++ b/arch/arm/mach-rk29/board-rk29-ddr3sdk.c @@ -66,6 +66,9 @@ #ifdef CONFIG_BU92747GUW_CIR #include "../../../drivers/cir/bu92747guw_cir.h" #endif +#include + + #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 diff --git a/arch/arm/mach-rk29/devices.c b/arch/arm/mach-rk29/devices.c index 779a9f13786c..26cdb9cbd548 100644 --- a/arch/arm/mach-rk29/devices.c +++ b/arch/arm/mach-rk29/devices.c @@ -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); diff --git a/arch/arm/mach-rk30/Makefile b/arch/arm/mach-rk30/Makefile index 0b585d466739..90c0919034db 100755 --- a/arch/arm/mach-rk30/Makefile +++ b/arch/arm/mach-rk30/Makefile @@ -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 diff --git a/arch/arm/mach-rk30/board-rk30-sdk.c b/arch/arm/mach-rk30/board-rk30-sdk.c index 94bac7a6e2a8..8a484c8adca6 100755 --- a/arch/arm/mach-rk30/board-rk30-sdk.c +++ b/arch/arm/mach-rk30/board-rk30-sdk.c @@ -42,6 +42,8 @@ #include #include #include +#include + #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 diff --git a/arch/arm/mach-rk30/devices.c b/arch/arm/mach-rk30/devices.c index 852ded9a79aa..35ecb9984821 100755 --- a/arch/arm/mach-rk30/devices.c +++ b/arch/arm/mach-rk30/devices.c @@ -28,6 +28,7 @@ #include #include #include +#include #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; } diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 18df94586dbd..8a5b66980c65 100755 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -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 index 000000000000..cb78dc0f93bf --- /dev/null +++ b/include/linux/rfkill-rk.h @@ -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 +#include + +#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 */ + diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index 73111a5867ab..224786574160 100644 --- a/net/rfkill/Kconfig +++ b/net/rfkill/Kconfig @@ -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 + diff --git a/net/rfkill/Makefile b/net/rfkill/Makefile index 311768783f4a..186dbab8b90b 100644 --- a/net/rfkill/Makefile +++ b/net/rfkill/Makefile @@ -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 diff --git a/net/rfkill/core.c b/net/rfkill/core.c index cfd0c89edf68..df2dae6b2723 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -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 index 000000000000..7c81864acbc3 --- /dev/null +++ b/net/rfkill/rfkill-rk.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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"); + -- 2.34.1