13d0407baSopenharmony_ci/* 23d0407baSopenharmony_ci * Copyright (C) 2012 ROCKCHIP, Inc. 33d0407baSopenharmony_ci * 43d0407baSopenharmony_ci * This software is licensed under the terms of the GNU General Public 53d0407baSopenharmony_ci * License version 2, as published by the Free Software Foundation, and 63d0407baSopenharmony_ci * may be copied, distributed, and modified under those terms. 73d0407baSopenharmony_ci * 83d0407baSopenharmony_ci * This program is distributed in the hope that it will be useful, 93d0407baSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 103d0407baSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 113d0407baSopenharmony_ci * GNU General Public License for more details. 123d0407baSopenharmony_ci * 133d0407baSopenharmony_ci */ 143d0407baSopenharmony_ci/* Rock-chips rfkill driver for bluetooth 153d0407baSopenharmony_ci * 163d0407baSopenharmony_ci*/ 173d0407baSopenharmony_ci 183d0407baSopenharmony_ci#include <linux/kernel.h> 193d0407baSopenharmony_ci#include <linux/platform_device.h> 203d0407baSopenharmony_ci#include <linux/module.h> 213d0407baSopenharmony_ci#include <linux/rfkill.h> 223d0407baSopenharmony_ci#include <linux/init.h> 233d0407baSopenharmony_ci#include <linux/slab.h> 243d0407baSopenharmony_ci#include <linux/delay.h> 253d0407baSopenharmony_ci#include <linux/rfkill-bt.h> 263d0407baSopenharmony_ci#include <linux/rfkill-wlan.h> 273d0407baSopenharmony_ci#include <linux/wakelock.h> 283d0407baSopenharmony_ci#include <linux/interrupt.h> 293d0407baSopenharmony_ci#include <asm/irq.h> 303d0407baSopenharmony_ci#include <linux/suspend.h> 313d0407baSopenharmony_ci#include <linux/proc_fs.h> 323d0407baSopenharmony_ci#include <linux/uaccess.h> 333d0407baSopenharmony_ci#include <linux/gpio.h> 343d0407baSopenharmony_ci#include <linux/fs.h> 353d0407baSopenharmony_ci#include <dt-bindings/gpio/gpio.h> 363d0407baSopenharmony_ci#include <uapi/linux/rfkill.h> 373d0407baSopenharmony_ci#ifdef CONFIG_OF 383d0407baSopenharmony_ci#include <linux/of.h> 393d0407baSopenharmony_ci#include <linux/of_device.h> 403d0407baSopenharmony_ci#include <linux/of_gpio.h> 413d0407baSopenharmony_ci#endif 423d0407baSopenharmony_ci 433d0407baSopenharmony_ci#if 0 443d0407baSopenharmony_ci#define DBG(x...) pr_info("[BT_RFKILL]: " x) 453d0407baSopenharmony_ci#else 463d0407baSopenharmony_ci#define DBG(x...) 473d0407baSopenharmony_ci#endif 483d0407baSopenharmony_ci 493d0407baSopenharmony_ci#define LOG(x...) pr_info("[BT_RFKILL]: " x) 503d0407baSopenharmony_ci 513d0407baSopenharmony_ci#define BT_WAKEUP_TIMEOUT 10000 523d0407baSopenharmony_ci#define BT_IRQ_WAKELOCK_TIMEOUT (10 * 1000) 533d0407baSopenharmony_ci 543d0407baSopenharmony_ci#define BT_BLOCKED true 553d0407baSopenharmony_ci#define BT_UNBLOCK false 563d0407baSopenharmony_ci#define BT_SLEEP true 573d0407baSopenharmony_ci#define BT_WAKEUP false 583d0407baSopenharmony_ci 593d0407baSopenharmony_cienum { 603d0407baSopenharmony_ci IOMUX_FNORMAL = 0, 613d0407baSopenharmony_ci IOMUX_FGPIO, 623d0407baSopenharmony_ci IOMUX_FMUX, 633d0407baSopenharmony_ci}; 643d0407baSopenharmony_ci 653d0407baSopenharmony_cistruct rfkill_rk_data { 663d0407baSopenharmony_ci struct rfkill_rk_platform_data *pdata; 673d0407baSopenharmony_ci struct platform_device *pdev; 683d0407baSopenharmony_ci struct rfkill *rfkill_dev; 693d0407baSopenharmony_ci struct wake_lock bt_irq_wl; 703d0407baSopenharmony_ci struct delayed_work bt_sleep_delay_work; 713d0407baSopenharmony_ci int irq_req; 723d0407baSopenharmony_ci}; 733d0407baSopenharmony_ci 743d0407baSopenharmony_cistatic struct rfkill_rk_data *g_rfkill = NULL; 753d0407baSopenharmony_ci 763d0407baSopenharmony_cistatic const char bt_name[] = 773d0407baSopenharmony_ci#if defined(CONFIG_BCM4330) 783d0407baSopenharmony_ci#if defined(CONFIG_BT_MODULE_NH660) 793d0407baSopenharmony_ci "nh660" 803d0407baSopenharmony_ci#else 813d0407baSopenharmony_ci "bcm4330" 823d0407baSopenharmony_ci#endif 833d0407baSopenharmony_ci#elif defined(CONFIG_RK903) 843d0407baSopenharmony_ci#if defined(CONFIG_RKWIFI_26M) 853d0407baSopenharmony_ci "rk903_26M" 863d0407baSopenharmony_ci#else 873d0407baSopenharmony_ci "rk903" 883d0407baSopenharmony_ci#endif 893d0407baSopenharmony_ci#elif defined(CONFIG_BCM4329) 903d0407baSopenharmony_ci "bcm4329" 913d0407baSopenharmony_ci#elif defined(CONFIG_MV8787) 923d0407baSopenharmony_ci "mv8787" 933d0407baSopenharmony_ci#elif defined(CONFIG_AP6210) 943d0407baSopenharmony_ci#if defined(CONFIG_RKWIFI_26M) 953d0407baSopenharmony_ci "ap6210" 963d0407baSopenharmony_ci#else 973d0407baSopenharmony_ci "ap6210_24M" 983d0407baSopenharmony_ci#endif 993d0407baSopenharmony_ci#elif defined(CONFIG_AP6330) 1003d0407baSopenharmony_ci "ap6330" 1013d0407baSopenharmony_ci#elif defined(CONFIG_AP6476) 1023d0407baSopenharmony_ci "ap6476" 1033d0407baSopenharmony_ci#elif defined(CONFIG_AP6493) 1043d0407baSopenharmony_ci "ap6493" 1053d0407baSopenharmony_ci#elif defined(CONFIG_AP6441) 1063d0407baSopenharmony_ci "ap6441" 1073d0407baSopenharmony_ci#elif defined(CONFIG_AP6335) 1083d0407baSopenharmony_ci "ap6335" 1093d0407baSopenharmony_ci#elif defined(CONFIG_GB86302I) 1103d0407baSopenharmony_ci "gb86302i" 1113d0407baSopenharmony_ci#else 1123d0407baSopenharmony_ci "bt_default" 1133d0407baSopenharmony_ci#endif 1143d0407baSopenharmony_ci ; 1153d0407baSopenharmony_ci 1163d0407baSopenharmony_cistatic irqreturn_t rfkill_rk_wake_host_irq(int irq, void *dev) 1173d0407baSopenharmony_ci{ 1183d0407baSopenharmony_ci struct rfkill_rk_data *rfkill = dev; 1193d0407baSopenharmony_ci 1203d0407baSopenharmony_ci LOG("BT_WAKE_HOST IRQ fired\n"); 1213d0407baSopenharmony_ci 1223d0407baSopenharmony_ci DBG("BT IRQ wakeup, request %dms wakelock\n", BT_IRQ_WAKELOCK_TIMEOUT); 1233d0407baSopenharmony_ci 1243d0407baSopenharmony_ci wake_lock_timeout(&rfkill->bt_irq_wl, 1253d0407baSopenharmony_ci msecs_to_jiffies(BT_IRQ_WAKELOCK_TIMEOUT)); 1263d0407baSopenharmony_ci 1273d0407baSopenharmony_ci return IRQ_HANDLED; 1283d0407baSopenharmony_ci} 1293d0407baSopenharmony_ci 1303d0407baSopenharmony_cistatic int rfkill_rk_setup_gpio(struct platform_device *pdev, 1313d0407baSopenharmony_ci struct rfkill_rk_gpio *gpio, const char *prefix, 1323d0407baSopenharmony_ci const char *name) 1333d0407baSopenharmony_ci{ 1343d0407baSopenharmony_ci if (gpio_is_valid(gpio->io)) { 1353d0407baSopenharmony_ci int ret = 0; 1363d0407baSopenharmony_ci 1373d0407baSopenharmony_ci sprintf(gpio->name, "%s_%s", prefix, name); 1383d0407baSopenharmony_ci ret = devm_gpio_request(&pdev->dev, gpio->io, gpio->name); 1393d0407baSopenharmony_ci if (ret) { 1403d0407baSopenharmony_ci LOG("Failed to get %s gpio.\n", gpio->name); 1413d0407baSopenharmony_ci return -1; 1423d0407baSopenharmony_ci } 1433d0407baSopenharmony_ci } 1443d0407baSopenharmony_ci 1453d0407baSopenharmony_ci return 0; 1463d0407baSopenharmony_ci} 1473d0407baSopenharmony_ci 1483d0407baSopenharmony_cistatic int rfkill_rk_setup_wake_irq(struct rfkill_rk_data *rfkill, int flag) 1493d0407baSopenharmony_ci{ 1503d0407baSopenharmony_ci int ret = 0; 1513d0407baSopenharmony_ci struct rfkill_rk_irq *irq = &rfkill->pdata->wake_host_irq; 1523d0407baSopenharmony_ci 1533d0407baSopenharmony_ci if (!flag) { 1543d0407baSopenharmony_ci rfkill->irq_req = 0; 1553d0407baSopenharmony_ci ret = rfkill_rk_setup_gpio(rfkill->pdev, &irq->gpio, 1563d0407baSopenharmony_ci rfkill->pdata->name, "wake_host"); 1573d0407baSopenharmony_ci if (ret) 1583d0407baSopenharmony_ci goto fail1; 1593d0407baSopenharmony_ci } 1603d0407baSopenharmony_ci if (gpio_is_valid(irq->gpio.io)) { 1613d0407baSopenharmony_ci if (rfkill->irq_req) { 1623d0407baSopenharmony_ci rfkill->irq_req = 0; 1633d0407baSopenharmony_ci free_irq(irq->irq, rfkill); 1643d0407baSopenharmony_ci } 1653d0407baSopenharmony_ci LOG("Request irq for bt wakeup host\n"); 1663d0407baSopenharmony_ci irq->irq = gpio_to_irq(irq->gpio.io); 1673d0407baSopenharmony_ci sprintf(irq->name, "%s_irq", irq->gpio.name); 1683d0407baSopenharmony_ci ret = request_irq(irq->irq, rfkill_rk_wake_host_irq, 1693d0407baSopenharmony_ci (irq->gpio.enable == GPIO_ACTIVE_LOW) ? 1703d0407baSopenharmony_ci IRQF_TRIGGER_FALLING : 1713d0407baSopenharmony_ci IRQF_TRIGGER_RISING, 1723d0407baSopenharmony_ci irq->name, rfkill); 1733d0407baSopenharmony_ci if (ret) 1743d0407baSopenharmony_ci goto fail2; 1753d0407baSopenharmony_ci rfkill->irq_req = 1; 1763d0407baSopenharmony_ci LOG("** disable irq\n"); 1773d0407baSopenharmony_ci disable_irq(irq->irq); 1783d0407baSopenharmony_ci ret = enable_irq_wake(irq->irq); 1793d0407baSopenharmony_ci if (ret) 1803d0407baSopenharmony_ci goto fail3; 1813d0407baSopenharmony_ci } 1823d0407baSopenharmony_ci 1833d0407baSopenharmony_ci return ret; 1843d0407baSopenharmony_ci 1853d0407baSopenharmony_cifail3: 1863d0407baSopenharmony_ci free_irq(irq->irq, rfkill); 1873d0407baSopenharmony_cifail2: 1883d0407baSopenharmony_ci gpio_free(irq->gpio.io); 1893d0407baSopenharmony_cifail1: 1903d0407baSopenharmony_ci return ret; 1913d0407baSopenharmony_ci} 1923d0407baSopenharmony_ci 1933d0407baSopenharmony_cistatic inline void rfkill_rk_sleep_bt_internal(struct rfkill_rk_data *rfkill, 1943d0407baSopenharmony_ci bool sleep) 1953d0407baSopenharmony_ci{ 1963d0407baSopenharmony_ci struct rfkill_rk_gpio *wake = &rfkill->pdata->wake_gpio; 1973d0407baSopenharmony_ci 1983d0407baSopenharmony_ci DBG("*** bt sleep: %d ***\n", sleep); 1993d0407baSopenharmony_ci#ifndef CONFIG_BK3515A_COMBO 2003d0407baSopenharmony_ci gpio_direction_output(wake->io, sleep ? !wake->enable : wake->enable); 2013d0407baSopenharmony_ci#else 2023d0407baSopenharmony_ci if (!sleep) { 2033d0407baSopenharmony_ci DBG("HOST_UART0_TX pull down 10us\n"); 2043d0407baSopenharmony_ci if (rfkill_rk_setup_gpio(rfkill->pdev, wake, 2053d0407baSopenharmony_ci rfkill->pdata->name, "wake") != 0) { 2063d0407baSopenharmony_ci return; 2073d0407baSopenharmony_ci } 2083d0407baSopenharmony_ci 2093d0407baSopenharmony_ci gpio_direction_output(wake->io, wake->enable); 2103d0407baSopenharmony_ci usleep_range(10, 20); 2113d0407baSopenharmony_ci gpio_direction_output(wake->io, !wake->enable); 2123d0407baSopenharmony_ci 2133d0407baSopenharmony_ci gpio_free(wake->io); 2143d0407baSopenharmony_ci } 2153d0407baSopenharmony_ci#endif 2163d0407baSopenharmony_ci} 2173d0407baSopenharmony_ci 2183d0407baSopenharmony_cistatic void rfkill_rk_delay_sleep_bt(struct work_struct *work) 2193d0407baSopenharmony_ci{ 2203d0407baSopenharmony_ci struct rfkill_rk_data *rfkill = NULL; 2213d0407baSopenharmony_ci 2223d0407baSopenharmony_ci DBG("Enter %s\n", __func__); 2233d0407baSopenharmony_ci 2243d0407baSopenharmony_ci rfkill = container_of(work, struct rfkill_rk_data, 2253d0407baSopenharmony_ci bt_sleep_delay_work.work); 2263d0407baSopenharmony_ci 2273d0407baSopenharmony_ci rfkill_rk_sleep_bt_internal(rfkill, BT_SLEEP); 2283d0407baSopenharmony_ci} 2293d0407baSopenharmony_ci 2303d0407baSopenharmony_civoid rfkill_rk_sleep_bt(bool sleep) 2313d0407baSopenharmony_ci{ 2323d0407baSopenharmony_ci struct rfkill_rk_data *rfkill = g_rfkill; 2333d0407baSopenharmony_ci struct rfkill_rk_gpio *wake; 2343d0407baSopenharmony_ci 2353d0407baSopenharmony_ci DBG("Enter %s\n", __func__); 2363d0407baSopenharmony_ci 2373d0407baSopenharmony_ci if (!rfkill) { 2383d0407baSopenharmony_ci LOG("*** RFKILL is empty???\n"); 2393d0407baSopenharmony_ci return; 2403d0407baSopenharmony_ci } 2413d0407baSopenharmony_ci 2423d0407baSopenharmony_ci wake = &rfkill->pdata->wake_gpio; 2433d0407baSopenharmony_ci if (!gpio_is_valid(wake->io)) { 2443d0407baSopenharmony_ci DBG("*** Not support bt wakeup and sleep\n"); 2453d0407baSopenharmony_ci return; 2463d0407baSopenharmony_ci } 2473d0407baSopenharmony_ci 2483d0407baSopenharmony_ci cancel_delayed_work_sync(&rfkill->bt_sleep_delay_work); 2493d0407baSopenharmony_ci 2503d0407baSopenharmony_ci rfkill_rk_sleep_bt_internal(rfkill, sleep); 2513d0407baSopenharmony_ci 2523d0407baSopenharmony_ci#ifdef CONFIG_BT_AUTOSLEEP 2533d0407baSopenharmony_ci if (sleep == BT_WAKEUP) { 2543d0407baSopenharmony_ci schedule_delayed_work(&rfkill->bt_sleep_delay_work, 2553d0407baSopenharmony_ci msecs_to_jiffies(BT_WAKEUP_TIMEOUT)); 2563d0407baSopenharmony_ci } 2573d0407baSopenharmony_ci#endif 2583d0407baSopenharmony_ci} 2593d0407baSopenharmony_ciEXPORT_SYMBOL(rfkill_rk_sleep_bt); 2603d0407baSopenharmony_ci 2613d0407baSopenharmony_cistatic int bt_power_state = 0; 2623d0407baSopenharmony_ciint rfkill_get_bt_power_state(int *power, bool *toggle) 2633d0407baSopenharmony_ci{ 2643d0407baSopenharmony_ci struct rfkill_rk_data *mrfkill = g_rfkill; 2653d0407baSopenharmony_ci 2663d0407baSopenharmony_ci if (!mrfkill) { 2673d0407baSopenharmony_ci LOG("%s: rfkill-bt driver has not Successful initialized\n", 2683d0407baSopenharmony_ci __func__); 2693d0407baSopenharmony_ci return -1; 2703d0407baSopenharmony_ci } 2713d0407baSopenharmony_ci 2723d0407baSopenharmony_ci *toggle = mrfkill->pdata->power_toggle; 2733d0407baSopenharmony_ci *power = bt_power_state; 2743d0407baSopenharmony_ci 2753d0407baSopenharmony_ci return 0; 2763d0407baSopenharmony_ci} 2773d0407baSopenharmony_ci 2783d0407baSopenharmony_cistatic int rfkill_rk_set_power(void *data, bool blocked) 2793d0407baSopenharmony_ci{ 2803d0407baSopenharmony_ci struct rfkill_rk_data *rfkill = data; 2813d0407baSopenharmony_ci struct rfkill_rk_gpio *wake_host = &rfkill->pdata->wake_host_irq.gpio; 2823d0407baSopenharmony_ci struct rfkill_rk_gpio *poweron = &rfkill->pdata->poweron_gpio; 2833d0407baSopenharmony_ci struct rfkill_rk_gpio *reset = &rfkill->pdata->reset_gpio; 2843d0407baSopenharmony_ci struct rfkill_rk_gpio *rts = &rfkill->pdata->rts_gpio; 2853d0407baSopenharmony_ci struct pinctrl *pinctrl = rfkill->pdata->pinctrl; 2863d0407baSopenharmony_ci int wifi_power = 0; 2873d0407baSopenharmony_ci bool toggle = false; 2883d0407baSopenharmony_ci 2893d0407baSopenharmony_ci DBG("Enter %s\n", __func__); 2903d0407baSopenharmony_ci 2913d0407baSopenharmony_ci DBG("Set blocked:%d\n", blocked); 2923d0407baSopenharmony_ci 2933d0407baSopenharmony_ci toggle = rfkill->pdata->power_toggle; 2943d0407baSopenharmony_ci 2953d0407baSopenharmony_ci if (toggle) { 2963d0407baSopenharmony_ci if (rfkill_get_wifi_power_state(&wifi_power)) { 2973d0407baSopenharmony_ci LOG("%s: cannot get wifi power state!\n", __func__); 2983d0407baSopenharmony_ci return -1; 2993d0407baSopenharmony_ci } 3003d0407baSopenharmony_ci } 3013d0407baSopenharmony_ci 3023d0407baSopenharmony_ci DBG("%s: toggle = %s\n", __func__, toggle ? "true" : "false"); 3033d0407baSopenharmony_ci 3043d0407baSopenharmony_ci if (!blocked) { 3053d0407baSopenharmony_ci if (toggle) { 3063d0407baSopenharmony_ci rfkill_set_wifi_bt_power(1); 3073d0407baSopenharmony_ci msleep(100); 3083d0407baSopenharmony_ci } 3093d0407baSopenharmony_ci 3103d0407baSopenharmony_ci rfkill_rk_sleep_bt(BT_WAKEUP); // ensure bt is wakeup 3113d0407baSopenharmony_ci 3123d0407baSopenharmony_ci if (gpio_is_valid(wake_host->io)) { 3133d0407baSopenharmony_ci LOG("%s: set bt wake_host high!\n", __func__); 3143d0407baSopenharmony_ci gpio_direction_output(wake_host->io, 1); 3153d0407baSopenharmony_ci msleep(20); 3163d0407baSopenharmony_ci } 3173d0407baSopenharmony_ci 3183d0407baSopenharmony_ci if (gpio_is_valid(poweron->io)) { 3193d0407baSopenharmony_ci if (gpio_get_value(poweron->io) == !poweron->enable) { 3203d0407baSopenharmony_ci gpio_direction_output(poweron->io, 3213d0407baSopenharmony_ci !poweron->enable); 3223d0407baSopenharmony_ci msleep(20); 3233d0407baSopenharmony_ci gpio_direction_output(poweron->io, 3243d0407baSopenharmony_ci poweron->enable); 3253d0407baSopenharmony_ci msleep(20); 3263d0407baSopenharmony_ci if (gpio_is_valid(wake_host->io)) 3273d0407baSopenharmony_ci gpio_direction_input(wake_host->io); 3283d0407baSopenharmony_ci } 3293d0407baSopenharmony_ci } 3303d0407baSopenharmony_ci 3313d0407baSopenharmony_ci if (gpio_is_valid(reset->io)) { 3323d0407baSopenharmony_ci if (gpio_get_value(reset->io) == !reset->enable) { 3333d0407baSopenharmony_ci gpio_direction_output(reset->io, 3343d0407baSopenharmony_ci !reset->enable); 3353d0407baSopenharmony_ci msleep(20); 3363d0407baSopenharmony_ci gpio_direction_output(reset->io, reset->enable); 3373d0407baSopenharmony_ci } 3383d0407baSopenharmony_ci } 3393d0407baSopenharmony_ci 3403d0407baSopenharmony_ci if (pinctrl && gpio_is_valid(rts->io)) { 3413d0407baSopenharmony_ci pinctrl_select_state(pinctrl, rts->gpio_state); 3423d0407baSopenharmony_ci LOG("ENABLE UART_RTS\n"); 3433d0407baSopenharmony_ci gpio_direction_output(rts->io, rts->enable); 3443d0407baSopenharmony_ci msleep(100); 3453d0407baSopenharmony_ci LOG("DISABLE UART_RTS\n"); 3463d0407baSopenharmony_ci gpio_direction_output(rts->io, !rts->enable); 3473d0407baSopenharmony_ci pinctrl_select_state(pinctrl, rts->default_state); 3483d0407baSopenharmony_ci } 3493d0407baSopenharmony_ci 3503d0407baSopenharmony_ci bt_power_state = 1; 3513d0407baSopenharmony_ci LOG("bt turn on power\n"); 3523d0407baSopenharmony_ci rfkill_rk_setup_wake_irq(rfkill, 1); 3533d0407baSopenharmony_ci } else { 3543d0407baSopenharmony_ci if (gpio_is_valid(poweron->io)) { 3553d0407baSopenharmony_ci if (gpio_get_value(poweron->io) == poweron->enable) { 3563d0407baSopenharmony_ci gpio_direction_output(poweron->io, 3573d0407baSopenharmony_ci !poweron->enable); 3583d0407baSopenharmony_ci msleep(20); 3593d0407baSopenharmony_ci } 3603d0407baSopenharmony_ci } 3613d0407baSopenharmony_ci 3623d0407baSopenharmony_ci bt_power_state = 0; 3633d0407baSopenharmony_ci LOG("bt shut off power\n"); 3643d0407baSopenharmony_ci if (gpio_is_valid(reset->io)) { 3653d0407baSopenharmony_ci if (gpio_get_value(reset->io) == reset->enable) { 3663d0407baSopenharmony_ci gpio_direction_output(reset->io, 3673d0407baSopenharmony_ci !reset->enable); 3683d0407baSopenharmony_ci msleep(20); 3693d0407baSopenharmony_ci } 3703d0407baSopenharmony_ci } 3713d0407baSopenharmony_ci if (toggle) { 3723d0407baSopenharmony_ci if (!wifi_power) { 3733d0407baSopenharmony_ci LOG("%s: bt will set vbat to low\n", __func__); 3743d0407baSopenharmony_ci rfkill_set_wifi_bt_power(0); 3753d0407baSopenharmony_ci } else { 3763d0407baSopenharmony_ci LOG("%s: bt shouldn't control the vbat\n", __func__); 3773d0407baSopenharmony_ci } 3783d0407baSopenharmony_ci } 3793d0407baSopenharmony_ci } 3803d0407baSopenharmony_ci 3813d0407baSopenharmony_ci return 0; 3823d0407baSopenharmony_ci} 3833d0407baSopenharmony_ci 3843d0407baSopenharmony_cistatic int rfkill_rk_pm_prepare(struct device *dev) 3853d0407baSopenharmony_ci{ 3863d0407baSopenharmony_ci struct rfkill_rk_data *rfkill = g_rfkill; 3873d0407baSopenharmony_ci struct rfkill_rk_gpio *rts; 3883d0407baSopenharmony_ci struct rfkill_rk_irq *wake_host_irq; 3893d0407baSopenharmony_ci 3903d0407baSopenharmony_ci DBG("Enter %s\n", __func__); 3913d0407baSopenharmony_ci 3923d0407baSopenharmony_ci if (!rfkill) 3933d0407baSopenharmony_ci return 0; 3943d0407baSopenharmony_ci 3953d0407baSopenharmony_ci rts = &rfkill->pdata->rts_gpio; 3963d0407baSopenharmony_ci wake_host_irq = &rfkill->pdata->wake_host_irq; 3973d0407baSopenharmony_ci 3983d0407baSopenharmony_ci //To prevent uart to receive bt data when suspended 3993d0407baSopenharmony_ci if (rfkill->pdata->pinctrl && gpio_is_valid(rts->io)) { 4003d0407baSopenharmony_ci DBG("Disable UART_RTS\n"); 4013d0407baSopenharmony_ci pinctrl_select_state(rfkill->pdata->pinctrl, rts->gpio_state); 4023d0407baSopenharmony_ci gpio_direction_output(rts->io, !rts->enable); 4033d0407baSopenharmony_ci } 4043d0407baSopenharmony_ci 4053d0407baSopenharmony_ci#ifdef CONFIG_BT_AUTOSLEEP 4063d0407baSopenharmony_ci rfkill_rk_sleep_bt(BT_SLEEP); 4073d0407baSopenharmony_ci#endif 4083d0407baSopenharmony_ci 4093d0407baSopenharmony_ci // enable bt wakeup host 4103d0407baSopenharmony_ci if (gpio_is_valid(wake_host_irq->gpio.io) && bt_power_state) { 4113d0407baSopenharmony_ci DBG("enable irq for bt wakeup host\n"); 4123d0407baSopenharmony_ci enable_irq(wake_host_irq->irq); 4133d0407baSopenharmony_ci } 4143d0407baSopenharmony_ci 4153d0407baSopenharmony_ci#ifdef CONFIG_RFKILL_RESET 4163d0407baSopenharmony_ci rfkill_init_sw_state(rfkill->rfkill_dev, BT_BLOCKED); 4173d0407baSopenharmony_ci rfkill_set_sw_state(rfkill->rfkill_dev, BT_BLOCKED); 4183d0407baSopenharmony_ci rfkill_set_hw_state(rfkill->rfkill_dev, false); 4193d0407baSopenharmony_ci rfkill_rk_set_power(rfkill, BT_BLOCKED); 4203d0407baSopenharmony_ci#endif 4213d0407baSopenharmony_ci 4223d0407baSopenharmony_ci return 0; 4233d0407baSopenharmony_ci} 4243d0407baSopenharmony_ci 4253d0407baSopenharmony_cistatic void rfkill_rk_pm_complete(struct device *dev) 4263d0407baSopenharmony_ci{ 4273d0407baSopenharmony_ci struct rfkill_rk_data *rfkill = g_rfkill; 4283d0407baSopenharmony_ci struct rfkill_rk_irq *wake_host_irq; 4293d0407baSopenharmony_ci struct rfkill_rk_gpio *rts; 4303d0407baSopenharmony_ci 4313d0407baSopenharmony_ci DBG("Enter %s\n", __func__); 4323d0407baSopenharmony_ci 4333d0407baSopenharmony_ci if (!rfkill) 4343d0407baSopenharmony_ci return; 4353d0407baSopenharmony_ci 4363d0407baSopenharmony_ci wake_host_irq = &rfkill->pdata->wake_host_irq; 4373d0407baSopenharmony_ci rts = &rfkill->pdata->rts_gpio; 4383d0407baSopenharmony_ci 4393d0407baSopenharmony_ci if (gpio_is_valid(wake_host_irq->gpio.io) && bt_power_state) { 4403d0407baSopenharmony_ci LOG("** disable irq\n"); 4413d0407baSopenharmony_ci disable_irq(wake_host_irq->irq); 4423d0407baSopenharmony_ci } 4433d0407baSopenharmony_ci 4443d0407baSopenharmony_ci if (rfkill->pdata->pinctrl && gpio_is_valid(rts->io)) { 4453d0407baSopenharmony_ci DBG("Enable UART_RTS\n"); 4463d0407baSopenharmony_ci gpio_direction_output(rts->io, rts->enable); 4473d0407baSopenharmony_ci pinctrl_select_state(rfkill->pdata->pinctrl, rts->default_state); 4483d0407baSopenharmony_ci } 4493d0407baSopenharmony_ci} 4503d0407baSopenharmony_ci 4513d0407baSopenharmony_cistatic const struct rfkill_ops rfkill_rk_ops = { 4523d0407baSopenharmony_ci .set_block = rfkill_rk_set_power, 4533d0407baSopenharmony_ci}; 4543d0407baSopenharmony_ci 4553d0407baSopenharmony_ci#define PROC_DIR "bluetooth/sleep" 4563d0407baSopenharmony_ci 4573d0407baSopenharmony_cistatic struct proc_dir_entry *bluetooth_dir, *sleep_dir; 4583d0407baSopenharmony_ci 4593d0407baSopenharmony_cistatic ssize_t bluesleep_read_proc_lpm(struct file *file, char __user *buffer, 4603d0407baSopenharmony_ci size_t count, loff_t *data) 4613d0407baSopenharmony_ci{ 4623d0407baSopenharmony_ci return sprintf(buffer, "unsupported to read\n"); 4633d0407baSopenharmony_ci} 4643d0407baSopenharmony_ci 4653d0407baSopenharmony_cistatic ssize_t bluesleep_write_proc_lpm(struct file *file, 4663d0407baSopenharmony_ci const char __user *buffer, size_t count, 4673d0407baSopenharmony_ci loff_t *data) 4683d0407baSopenharmony_ci{ 4693d0407baSopenharmony_ci return count; 4703d0407baSopenharmony_ci} 4713d0407baSopenharmony_ci 4723d0407baSopenharmony_cistatic ssize_t bluesleep_read_proc_btwrite(struct file *file, 4733d0407baSopenharmony_ci char __user *buffer, size_t count, 4743d0407baSopenharmony_ci loff_t *data) 4753d0407baSopenharmony_ci{ 4763d0407baSopenharmony_ci return sprintf(buffer, "unsupported to read\n"); 4773d0407baSopenharmony_ci} 4783d0407baSopenharmony_ci 4793d0407baSopenharmony_cistatic ssize_t bluesleep_write_proc_btwrite(struct file *file, 4803d0407baSopenharmony_ci const char __user *buffer, 4813d0407baSopenharmony_ci size_t count, loff_t *data) 4823d0407baSopenharmony_ci{ 4833d0407baSopenharmony_ci char b; 4843d0407baSopenharmony_ci 4853d0407baSopenharmony_ci if (count < 1) 4863d0407baSopenharmony_ci return -EINVAL; 4873d0407baSopenharmony_ci 4883d0407baSopenharmony_ci if (copy_from_user(&b, buffer, 1)) 4893d0407baSopenharmony_ci return -EFAULT; 4903d0407baSopenharmony_ci 4913d0407baSopenharmony_ci DBG("btwrite %c\n", b); 4923d0407baSopenharmony_ci /* HCI_DEV_WRITE */ 4933d0407baSopenharmony_ci if (b != '0') 4943d0407baSopenharmony_ci rfkill_rk_sleep_bt(BT_WAKEUP); 4953d0407baSopenharmony_ci else 4963d0407baSopenharmony_ci rfkill_rk_sleep_bt(BT_SLEEP); 4973d0407baSopenharmony_ci 4983d0407baSopenharmony_ci return count; 4993d0407baSopenharmony_ci} 5003d0407baSopenharmony_ci 5013d0407baSopenharmony_ci#ifdef CONFIG_OF 5023d0407baSopenharmony_cistatic int bluetooth_platdata_parse_dt(struct device *dev, 5033d0407baSopenharmony_ci struct rfkill_rk_platform_data *data) 5043d0407baSopenharmony_ci{ 5053d0407baSopenharmony_ci struct device_node *node = dev->of_node; 5063d0407baSopenharmony_ci int gpio; 5073d0407baSopenharmony_ci enum of_gpio_flags flags; 5083d0407baSopenharmony_ci 5093d0407baSopenharmony_ci if (!node) 5103d0407baSopenharmony_ci return -ENODEV; 5113d0407baSopenharmony_ci 5123d0407baSopenharmony_ci memset(data, 0, sizeof(*data)); 5133d0407baSopenharmony_ci 5143d0407baSopenharmony_ci if (of_find_property(node, "wifi-bt-power-toggle", NULL)) { 5153d0407baSopenharmony_ci data->power_toggle = true; 5163d0407baSopenharmony_ci LOG("%s: get property wifi-bt-power-toggle.\n", __func__); 5173d0407baSopenharmony_ci } else { 5183d0407baSopenharmony_ci data->power_toggle = false; 5193d0407baSopenharmony_ci } 5203d0407baSopenharmony_ci 5213d0407baSopenharmony_ci gpio = of_get_named_gpio_flags(node, "uart_rts_gpios", 0, &flags); 5223d0407baSopenharmony_ci if (gpio_is_valid(gpio)) { 5233d0407baSopenharmony_ci data->rts_gpio.io = gpio; 5243d0407baSopenharmony_ci data->rts_gpio.enable = (flags == GPIO_ACTIVE_HIGH) ? 1 : 0; 5253d0407baSopenharmony_ci LOG("%s: get property: uart_rts_gpios = %d.\n", __func__, gpio); 5263d0407baSopenharmony_ci data->pinctrl = devm_pinctrl_get(dev); 5273d0407baSopenharmony_ci if (!IS_ERR(data->pinctrl)) { 5283d0407baSopenharmony_ci data->rts_gpio.default_state = 5293d0407baSopenharmony_ci pinctrl_lookup_state(data->pinctrl, "default"); 5303d0407baSopenharmony_ci data->rts_gpio.gpio_state = 5313d0407baSopenharmony_ci pinctrl_lookup_state(data->pinctrl, "rts_gpio"); 5323d0407baSopenharmony_ci } else { 5333d0407baSopenharmony_ci data->pinctrl = NULL; 5343d0407baSopenharmony_ci LOG("%s: dts does't define the uart rts iomux.\n", 5353d0407baSopenharmony_ci __func__); 5363d0407baSopenharmony_ci return -EINVAL; 5373d0407baSopenharmony_ci } 5383d0407baSopenharmony_ci } else { 5393d0407baSopenharmony_ci data->pinctrl = NULL; 5403d0407baSopenharmony_ci data->rts_gpio.io = -EINVAL; 5413d0407baSopenharmony_ci LOG("%s: uart_rts_gpios is no-in-use.\n", __func__); 5423d0407baSopenharmony_ci } 5433d0407baSopenharmony_ci 5443d0407baSopenharmony_ci gpio = of_get_named_gpio_flags(node, "BT,power_gpio", 0, &flags); 5453d0407baSopenharmony_ci if (gpio_is_valid(gpio)) { 5463d0407baSopenharmony_ci data->poweron_gpio.io = gpio; 5473d0407baSopenharmony_ci data->poweron_gpio.enable = (flags == GPIO_ACTIVE_HIGH) ? 1 : 0; 5483d0407baSopenharmony_ci LOG("%s: get property: BT,power_gpio = %d.\n", __func__, gpio); 5493d0407baSopenharmony_ci } else { 5503d0407baSopenharmony_ci data->poweron_gpio.io = -1; 5513d0407baSopenharmony_ci } 5523d0407baSopenharmony_ci gpio = of_get_named_gpio_flags(node, "BT,reset_gpio", 0, &flags); 5533d0407baSopenharmony_ci if (gpio_is_valid(gpio)) { 5543d0407baSopenharmony_ci data->reset_gpio.io = gpio; 5553d0407baSopenharmony_ci data->reset_gpio.enable = (flags == GPIO_ACTIVE_HIGH) ? 1 : 0; 5563d0407baSopenharmony_ci LOG("%s: get property: BT,reset_gpio = %d.\n", __func__, gpio); 5573d0407baSopenharmony_ci } else { 5583d0407baSopenharmony_ci data->reset_gpio.io = -1; 5593d0407baSopenharmony_ci } 5603d0407baSopenharmony_ci gpio = of_get_named_gpio_flags(node, "BT,wake_gpio", 0, &flags); 5613d0407baSopenharmony_ci if (gpio_is_valid(gpio)) { 5623d0407baSopenharmony_ci data->wake_gpio.io = gpio; 5633d0407baSopenharmony_ci data->wake_gpio.enable = (flags == GPIO_ACTIVE_HIGH) ? 1 : 0; 5643d0407baSopenharmony_ci LOG("%s: get property: BT,wake_gpio = %d.\n", __func__, gpio); 5653d0407baSopenharmony_ci } else { 5663d0407baSopenharmony_ci data->wake_gpio.io = -1; 5673d0407baSopenharmony_ci } 5683d0407baSopenharmony_ci gpio = of_get_named_gpio_flags(node, "BT,wake_host_irq", 0, &flags); 5693d0407baSopenharmony_ci if (gpio_is_valid(gpio)) { 5703d0407baSopenharmony_ci data->wake_host_irq.gpio.io = gpio; 5713d0407baSopenharmony_ci data->wake_host_irq.gpio.enable = flags; 5723d0407baSopenharmony_ci LOG("%s: get property: BT,wake_host_irq = %d.\n", __func__, 5733d0407baSopenharmony_ci gpio); 5743d0407baSopenharmony_ci } else { 5753d0407baSopenharmony_ci data->wake_host_irq.gpio.io = -1; 5763d0407baSopenharmony_ci } 5773d0407baSopenharmony_ci 5783d0407baSopenharmony_ci data->ext_clk = devm_clk_get(dev, "ext_clock"); 5793d0407baSopenharmony_ci if (IS_ERR(data->ext_clk)) { 5803d0407baSopenharmony_ci LOG("%s: clk_get failed!!!.\n", __func__); 5813d0407baSopenharmony_ci } else { 5823d0407baSopenharmony_ci clk_prepare_enable(data->ext_clk); 5833d0407baSopenharmony_ci } 5843d0407baSopenharmony_ci return 0; 5853d0407baSopenharmony_ci} 5863d0407baSopenharmony_ci#endif //CONFIG_OF 5873d0407baSopenharmony_ci 5883d0407baSopenharmony_cistatic const struct proc_ops bluesleep_lpm = { 5893d0407baSopenharmony_ci .proc_read = bluesleep_read_proc_lpm, 5903d0407baSopenharmony_ci .proc_write = bluesleep_write_proc_lpm, 5913d0407baSopenharmony_ci}; 5923d0407baSopenharmony_ci 5933d0407baSopenharmony_cistatic const struct proc_ops bluesleep_btwrite = { 5943d0407baSopenharmony_ci .proc_read = bluesleep_read_proc_btwrite, 5953d0407baSopenharmony_ci .proc_write = bluesleep_write_proc_btwrite, 5963d0407baSopenharmony_ci}; 5973d0407baSopenharmony_ci 5983d0407baSopenharmony_cistatic int rfkill_rk_probe(struct platform_device *pdev) 5993d0407baSopenharmony_ci{ 6003d0407baSopenharmony_ci struct rfkill_rk_data *rfkill; 6013d0407baSopenharmony_ci struct rfkill_rk_platform_data *pdata = pdev->dev.platform_data; 6023d0407baSopenharmony_ci int ret = 0; 6033d0407baSopenharmony_ci struct proc_dir_entry *ent; 6043d0407baSopenharmony_ci 6053d0407baSopenharmony_ci DBG("Enter %s\n", __func__); 6063d0407baSopenharmony_ci 6073d0407baSopenharmony_ci if (!pdata) { 6083d0407baSopenharmony_ci#ifdef CONFIG_OF 6093d0407baSopenharmony_ci pdata = devm_kzalloc(&pdev->dev, 6103d0407baSopenharmony_ci sizeof(struct rfkill_rk_platform_data), 6113d0407baSopenharmony_ci GFP_KERNEL); 6123d0407baSopenharmony_ci if (!pdata) 6133d0407baSopenharmony_ci return -ENOMEM; 6143d0407baSopenharmony_ci 6153d0407baSopenharmony_ci ret = bluetooth_platdata_parse_dt(&pdev->dev, pdata); 6163d0407baSopenharmony_ci if (ret < 0) { 6173d0407baSopenharmony_ci#endif 6183d0407baSopenharmony_ci LOG("%s: No platform data specified\n", __func__); 6193d0407baSopenharmony_ci return ret; 6203d0407baSopenharmony_ci#ifdef CONFIG_OF 6213d0407baSopenharmony_ci } 6223d0407baSopenharmony_ci#endif 6233d0407baSopenharmony_ci } 6243d0407baSopenharmony_ci 6253d0407baSopenharmony_ci pdata->name = (char *)bt_name; 6263d0407baSopenharmony_ci pdata->type = RFKILL_TYPE_BLUETOOTH; 6273d0407baSopenharmony_ci 6283d0407baSopenharmony_ci rfkill = devm_kzalloc(&pdev->dev, sizeof(*rfkill), GFP_KERNEL); 6293d0407baSopenharmony_ci if (!rfkill) 6303d0407baSopenharmony_ci return -ENOMEM; 6313d0407baSopenharmony_ci 6323d0407baSopenharmony_ci rfkill->pdata = pdata; 6333d0407baSopenharmony_ci rfkill->pdev = pdev; 6343d0407baSopenharmony_ci g_rfkill = rfkill; 6353d0407baSopenharmony_ci 6363d0407baSopenharmony_ci bluetooth_dir = proc_mkdir("bluetooth", NULL); 6373d0407baSopenharmony_ci if (!bluetooth_dir) { 6383d0407baSopenharmony_ci LOG("Unable to create /proc/bluetooth directory"); 6393d0407baSopenharmony_ci return -ENOMEM; 6403d0407baSopenharmony_ci } 6413d0407baSopenharmony_ci 6423d0407baSopenharmony_ci sleep_dir = proc_mkdir("sleep", bluetooth_dir); 6433d0407baSopenharmony_ci if (!sleep_dir) { 6443d0407baSopenharmony_ci LOG("Unable to create /proc/%s directory", PROC_DIR); 6453d0407baSopenharmony_ci return -ENOMEM; 6463d0407baSopenharmony_ci } 6473d0407baSopenharmony_ci 6483d0407baSopenharmony_ci /* read/write proc entries */ 6493d0407baSopenharmony_ci ent = proc_create("lpm", 0444, sleep_dir, &bluesleep_lpm); 6503d0407baSopenharmony_ci if (!ent) { 6513d0407baSopenharmony_ci LOG("Unable to create /proc/%s/lpm entry", PROC_DIR); 6523d0407baSopenharmony_ci ret = -ENOMEM; 6533d0407baSopenharmony_ci goto fail_alloc; 6543d0407baSopenharmony_ci } 6553d0407baSopenharmony_ci 6563d0407baSopenharmony_ci /* read/write proc entries */ 6573d0407baSopenharmony_ci ent = proc_create("btwrite", 0444, sleep_dir, &bluesleep_btwrite); 6583d0407baSopenharmony_ci if (!ent) { 6593d0407baSopenharmony_ci LOG("Unable to create /proc/%s/btwrite entry", PROC_DIR); 6603d0407baSopenharmony_ci ret = -ENOMEM; 6613d0407baSopenharmony_ci goto fail_alloc; 6623d0407baSopenharmony_ci } 6633d0407baSopenharmony_ci 6643d0407baSopenharmony_ci DBG("init gpio\n"); 6653d0407baSopenharmony_ci 6663d0407baSopenharmony_ci ret = rfkill_rk_setup_gpio(pdev, &pdata->poweron_gpio, pdata->name, 6673d0407baSopenharmony_ci "poweron"); 6683d0407baSopenharmony_ci if (ret) 6693d0407baSopenharmony_ci goto fail_gpio; 6703d0407baSopenharmony_ci 6713d0407baSopenharmony_ci ret = rfkill_rk_setup_gpio(pdev, &pdata->reset_gpio, pdata->name, 6723d0407baSopenharmony_ci "reset"); 6733d0407baSopenharmony_ci if (ret) 6743d0407baSopenharmony_ci goto fail_gpio; 6753d0407baSopenharmony_ci 6763d0407baSopenharmony_ci ret = rfkill_rk_setup_gpio(pdev, &pdata->wake_gpio, pdata->name, 6773d0407baSopenharmony_ci "wake"); 6783d0407baSopenharmony_ci if (ret) 6793d0407baSopenharmony_ci goto fail_gpio; 6803d0407baSopenharmony_ci 6813d0407baSopenharmony_ci ret = rfkill_rk_setup_gpio(pdev, &pdata->rts_gpio, rfkill->pdata->name, 6823d0407baSopenharmony_ci "rts"); 6833d0407baSopenharmony_ci if (ret) 6843d0407baSopenharmony_ci goto fail_gpio; 6853d0407baSopenharmony_ci 6863d0407baSopenharmony_ci wake_lock_init(&rfkill->bt_irq_wl, WAKE_LOCK_SUSPEND, 6873d0407baSopenharmony_ci "rfkill_rk_irq_wl"); 6883d0407baSopenharmony_ci 6893d0407baSopenharmony_ci ret = rfkill_rk_setup_wake_irq(rfkill, 0); 6903d0407baSopenharmony_ci if (ret) 6913d0407baSopenharmony_ci goto fail_setup_wake_irq; 6923d0407baSopenharmony_ci 6933d0407baSopenharmony_ci DBG("setup rfkill\n"); 6943d0407baSopenharmony_ci rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type, 6953d0407baSopenharmony_ci &rfkill_rk_ops, rfkill); 6963d0407baSopenharmony_ci if (!rfkill->rfkill_dev) 6973d0407baSopenharmony_ci goto fail_alloc; 6983d0407baSopenharmony_ci 6993d0407baSopenharmony_ci rfkill_init_sw_state(rfkill->rfkill_dev, BT_BLOCKED); 7003d0407baSopenharmony_ci rfkill_set_sw_state(rfkill->rfkill_dev, BT_BLOCKED); 7013d0407baSopenharmony_ci rfkill_set_hw_state(rfkill->rfkill_dev, false); 7023d0407baSopenharmony_ci ret = rfkill_register(rfkill->rfkill_dev); 7033d0407baSopenharmony_ci if (ret < 0) 7043d0407baSopenharmony_ci goto fail_rfkill; 7053d0407baSopenharmony_ci 7063d0407baSopenharmony_ci INIT_DELAYED_WORK(&rfkill->bt_sleep_delay_work, 7073d0407baSopenharmony_ci rfkill_rk_delay_sleep_bt); 7083d0407baSopenharmony_ci 7093d0407baSopenharmony_ci //rfkill_rk_set_power(rfkill, BT_BLOCKED); 7103d0407baSopenharmony_ci // bt turn off power 7113d0407baSopenharmony_ci if (gpio_is_valid(pdata->poweron_gpio.io)) { 7123d0407baSopenharmony_ci gpio_direction_output(pdata->poweron_gpio.io, 7133d0407baSopenharmony_ci !pdata->poweron_gpio.enable); 7143d0407baSopenharmony_ci } 7153d0407baSopenharmony_ci if (gpio_is_valid(pdata->reset_gpio.io)) { 7163d0407baSopenharmony_ci gpio_direction_output(pdata->reset_gpio.io, 7173d0407baSopenharmony_ci !pdata->reset_gpio.enable); 7183d0407baSopenharmony_ci } 7193d0407baSopenharmony_ci 7203d0407baSopenharmony_ci platform_set_drvdata(pdev, rfkill); 7213d0407baSopenharmony_ci 7223d0407baSopenharmony_ci LOG("%s device registered.\n", pdata->name); 7233d0407baSopenharmony_ci 7243d0407baSopenharmony_ci return 0; 7253d0407baSopenharmony_ci 7263d0407baSopenharmony_cifail_rfkill: 7273d0407baSopenharmony_ci rfkill_destroy(rfkill->rfkill_dev); 7283d0407baSopenharmony_cifail_alloc: 7293d0407baSopenharmony_ci 7303d0407baSopenharmony_ci remove_proc_entry("btwrite", sleep_dir); 7313d0407baSopenharmony_ci remove_proc_entry("lpm", sleep_dir); 7323d0407baSopenharmony_cifail_setup_wake_irq: 7333d0407baSopenharmony_ci wake_lock_destroy(&rfkill->bt_irq_wl); 7343d0407baSopenharmony_cifail_gpio: 7353d0407baSopenharmony_ci 7363d0407baSopenharmony_ci g_rfkill = NULL; 7373d0407baSopenharmony_ci return ret; 7383d0407baSopenharmony_ci} 7393d0407baSopenharmony_ci 7403d0407baSopenharmony_cistatic int rfkill_rk_remove(struct platform_device *pdev) 7413d0407baSopenharmony_ci{ 7423d0407baSopenharmony_ci struct rfkill_rk_data *rfkill = platform_get_drvdata(pdev); 7433d0407baSopenharmony_ci 7443d0407baSopenharmony_ci LOG("Enter %s\n", __func__); 7453d0407baSopenharmony_ci 7463d0407baSopenharmony_ci rfkill_unregister(rfkill->rfkill_dev); 7473d0407baSopenharmony_ci rfkill_destroy(rfkill->rfkill_dev); 7483d0407baSopenharmony_ci 7493d0407baSopenharmony_ci cancel_delayed_work_sync(&rfkill->bt_sleep_delay_work); 7503d0407baSopenharmony_ci 7513d0407baSopenharmony_ci // free gpio 7523d0407baSopenharmony_ci if (gpio_is_valid(rfkill->pdata->rts_gpio.io)) 7533d0407baSopenharmony_ci gpio_free(rfkill->pdata->rts_gpio.io); 7543d0407baSopenharmony_ci 7553d0407baSopenharmony_ci if (gpio_is_valid(rfkill->pdata->wake_host_irq.gpio.io)) { 7563d0407baSopenharmony_ci free_irq(rfkill->pdata->wake_host_irq.irq, rfkill); 7573d0407baSopenharmony_ci#ifndef CONFIG_BK3515A_COMBO 7583d0407baSopenharmony_ci gpio_free(rfkill->pdata->wake_host_irq.gpio.io); 7593d0407baSopenharmony_ci#endif 7603d0407baSopenharmony_ci } 7613d0407baSopenharmony_ci 7623d0407baSopenharmony_ci#ifndef CONFIG_BK3515A_COMBO 7633d0407baSopenharmony_ci if (gpio_is_valid(rfkill->pdata->wake_gpio.io)) 7643d0407baSopenharmony_ci gpio_free(rfkill->pdata->wake_gpio.io); 7653d0407baSopenharmony_ci#endif 7663d0407baSopenharmony_ci 7673d0407baSopenharmony_ci if (gpio_is_valid(rfkill->pdata->reset_gpio.io)) 7683d0407baSopenharmony_ci gpio_free(rfkill->pdata->reset_gpio.io); 7693d0407baSopenharmony_ci 7703d0407baSopenharmony_ci if (gpio_is_valid(rfkill->pdata->poweron_gpio.io)) 7713d0407baSopenharmony_ci gpio_free(rfkill->pdata->poweron_gpio.io); 7723d0407baSopenharmony_ci clk_disable_unprepare(rfkill->pdata->ext_clk); 7733d0407baSopenharmony_ci wake_lock_destroy(&rfkill->bt_irq_wl); 7743d0407baSopenharmony_ci g_rfkill = NULL; 7753d0407baSopenharmony_ci 7763d0407baSopenharmony_ci return 0; 7773d0407baSopenharmony_ci} 7783d0407baSopenharmony_ci 7793d0407baSopenharmony_cistatic const struct dev_pm_ops rfkill_rk_pm_ops = { 7803d0407baSopenharmony_ci .prepare = rfkill_rk_pm_prepare, 7813d0407baSopenharmony_ci .complete = rfkill_rk_pm_complete, 7823d0407baSopenharmony_ci}; 7833d0407baSopenharmony_ci 7843d0407baSopenharmony_ci#ifdef CONFIG_OF 7853d0407baSopenharmony_cistatic struct of_device_id bt_platdata_of_match[] = { 7863d0407baSopenharmony_ci { .compatible = "bluetooth-platdata" }, 7873d0407baSopenharmony_ci {} 7883d0407baSopenharmony_ci}; 7893d0407baSopenharmony_ciMODULE_DEVICE_TABLE(of, bt_platdata_of_match); 7903d0407baSopenharmony_ci#endif //CONFIG_OF 7913d0407baSopenharmony_ci 7923d0407baSopenharmony_cistatic struct platform_driver rfkill_rk_driver = { 7933d0407baSopenharmony_ci .probe = rfkill_rk_probe, 7943d0407baSopenharmony_ci .remove = rfkill_rk_remove, 7953d0407baSopenharmony_ci .driver = { 7963d0407baSopenharmony_ci .name = "rfkill_bt", 7973d0407baSopenharmony_ci .owner = THIS_MODULE, 7983d0407baSopenharmony_ci .pm = &rfkill_rk_pm_ops, 7993d0407baSopenharmony_ci .of_match_table = of_match_ptr(bt_platdata_of_match), 8003d0407baSopenharmony_ci }, 8013d0407baSopenharmony_ci}; 8023d0407baSopenharmony_ci 8033d0407baSopenharmony_cistatic int __init rfkill_rk_init(void) 8043d0407baSopenharmony_ci{ 8053d0407baSopenharmony_ci int err; 8063d0407baSopenharmony_ci 8073d0407baSopenharmony_ci LOG("Enter %s\n", __func__); 8083d0407baSopenharmony_ci err = rfkill_wlan_init(); 8093d0407baSopenharmony_ci if (err) 8103d0407baSopenharmony_ci return err; 8113d0407baSopenharmony_ci return platform_driver_register(&rfkill_rk_driver); 8123d0407baSopenharmony_ci} 8133d0407baSopenharmony_ci 8143d0407baSopenharmony_cistatic void __exit rfkill_rk_exit(void) 8153d0407baSopenharmony_ci{ 8163d0407baSopenharmony_ci LOG("Enter %s\n", __func__); 8173d0407baSopenharmony_ci platform_driver_unregister(&rfkill_rk_driver); 8183d0407baSopenharmony_ci rfkill_wlan_exit(); 8193d0407baSopenharmony_ci} 8203d0407baSopenharmony_ci 8213d0407baSopenharmony_cimodule_init(rfkill_rk_init); 8223d0407baSopenharmony_cimodule_exit(rfkill_rk_exit); 8233d0407baSopenharmony_ci 8243d0407baSopenharmony_ciMODULE_DESCRIPTION("rock-chips rfkill for Bluetooth v0.3"); 8253d0407baSopenharmony_ciMODULE_AUTHOR("cmy@rock-chips.com, gwl@rock-chips.com"); 8263d0407baSopenharmony_ciMODULE_LICENSE("GPL"); 827