mirror of
https://github.com/hardkernel/kernel_common_drivers.git
synced 2026-06-25 12:03:48 +09:00
522a4d86e9
PD#SWPL-142862 Problem: 1.bt open fail when rtc wakeup; Solution: 1.reset this state when rtc wakeup; Verify: ohm. Change-Id: If86fd7eef5926ee44a6740ba229ee040759389a4 Signed-off-by: Kuibao Zhang <kuibao.zhang@amlogic.com>
736 lines
17 KiB
C
736 lines
17 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
//#define DEBUG
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/string.h>
|
|
#include <linux/ctype.h>
|
|
#include <linux/leds.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/rfkill.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/of.h>
|
|
#include <linux/pinctrl/consumer.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/amlogic/cpu_version.h>
|
|
#include <linux/amlogic/iomap.h>
|
|
#include <linux/io.h>
|
|
#include <linux/amlogic/bt_device.h>
|
|
#include <linux/amlogic/wifi_dt.h>
|
|
#include <linux/random.h>
|
|
#ifdef CONFIG_AM_WIFI_SD_MMC
|
|
#include <linux/amlogic/wifi_dt.h>
|
|
#endif
|
|
#include <gpiolib.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
#include <linux/pm_wakeup.h>
|
|
#include <linux/pm_wakeirq.h>
|
|
#include <linux/amlogic/pm.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/amlogic/gki_module.h>
|
|
|
|
#include <linux/input.h>
|
|
|
|
#if defined(CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND) && defined(CONFIG_AMLOGIC_GX_SUSPEND)
|
|
#include <linux/amlogic/pm.h>
|
|
static struct early_suspend bt_early_suspend;
|
|
#endif
|
|
|
|
#define BT_RFKILL "bt_rfkill"
|
|
#define QCA_ID 0x271
|
|
#define AML_ID 0x8888
|
|
#define POWER_EVENT_DEF 0
|
|
#define POWER_EVENT_RESET 1
|
|
#define POWER_EVENT_EN 2
|
|
//#define CONTROL_POWER_EN_LINUX
|
|
|
|
char bt_addr[18] = "";
|
|
char *btmac;
|
|
//core_param(btmac, btmac, charp, 0644);
|
|
module_param(btmac, charp, 0644);
|
|
static struct class *bt_addr_class;
|
|
static int btwake_evt;
|
|
static int btirq_flag;
|
|
static int btpower_evt;
|
|
static int flag_n;
|
|
static int flag_p;
|
|
static int cnt;
|
|
|
|
static int distinguish_module(void)
|
|
{
|
|
int vendor_id = 0;
|
|
|
|
vendor_id = sdio_get_vendor();
|
|
pr_info("vendor_id = 0x%x\n", vendor_id);
|
|
|
|
if (vendor_id == QCA_ID)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t value_show(struct class *cls,
|
|
struct class_attribute *attr, char *_buf)
|
|
{
|
|
char local_addr[6];
|
|
|
|
if (!_buf)
|
|
return -EINVAL;
|
|
|
|
if (strlen(bt_addr) == 0) {
|
|
local_addr[0] = 0x22;
|
|
local_addr[1] = 0x22;
|
|
local_addr[2] = prandom_u32();
|
|
local_addr[3] = prandom_u32();
|
|
local_addr[4] = prandom_u32();
|
|
local_addr[5] = prandom_u32();
|
|
sprintf(bt_addr, "%02x:%02x:%02x:%02x:%02x:%02x",
|
|
local_addr[0], local_addr[1], local_addr[2],
|
|
local_addr[3], local_addr[4], local_addr[5]);
|
|
}
|
|
|
|
return sprintf(_buf, "%s\n", bt_addr);
|
|
}
|
|
|
|
static ssize_t value_store(struct class *cls,
|
|
struct class_attribute *attr,
|
|
const char __user *buf, size_t count)
|
|
{
|
|
int ret = -EINVAL;
|
|
|
|
if (!buf)
|
|
return ret;
|
|
|
|
snprintf(bt_addr, sizeof(bt_addr), "%s", buf);
|
|
|
|
if (bt_addr[strlen(bt_addr) - 1] == '\n')
|
|
bt_addr[strlen(bt_addr) - 1] = '\0';
|
|
|
|
pr_debug("bt_addr=%s\n", bt_addr);
|
|
return count;
|
|
}
|
|
static CLASS_ATTR_RW(value);
|
|
|
|
struct bt_dev_runtime_data {
|
|
struct rfkill *bt_rfk;
|
|
struct bt_dev_data *pdata;
|
|
};
|
|
|
|
static void off_def_power(struct bt_dev_data *pdata, unsigned long down_time)
|
|
{
|
|
pr_debug("[%s]: pid: %d comm: %s\n", __func__,
|
|
current->pid, current->comm);
|
|
|
|
if (pdata->gpio_reset > 0) {
|
|
if (pdata->power_on_pin_OD &&
|
|
pdata->power_low_level) {
|
|
gpio_direction_input(pdata->gpio_reset);
|
|
} else {
|
|
gpio_direction_output(pdata->gpio_reset,
|
|
pdata->power_low_level);
|
|
}
|
|
|
|
if (down_time)
|
|
msleep(down_time);
|
|
}
|
|
|
|
#ifndef CONTROL_POWER_EN_LINUX
|
|
if (pdata->gpio_en && pdata->power_on_pin_OD &&
|
|
pdata->power_low_level) {
|
|
gpio_direction_input(pdata->gpio_en);
|
|
} else {
|
|
set_usb_bt_power(0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void on_def_power(struct bt_dev_data *pdata, unsigned long up_time)
|
|
{
|
|
pr_debug("[%s]: pid: %d comm: %s\n", __func__,
|
|
current->pid, current->comm);
|
|
|
|
if (pdata->gpio_reset > 0) {
|
|
if (pdata->power_on_pin_OD &&
|
|
!pdata->power_low_level) {
|
|
gpio_direction_input(pdata->gpio_reset);
|
|
} else {
|
|
gpio_direction_output(pdata->gpio_reset,
|
|
!pdata->power_low_level);
|
|
}
|
|
|
|
if (up_time)
|
|
msleep(up_time);
|
|
}
|
|
|
|
#ifndef CONTROL_POWER_EN_LINUX
|
|
if (pdata->gpio_en && pdata->power_on_pin_OD &&
|
|
!pdata->power_low_level) {
|
|
gpio_direction_input(pdata->gpio_en);
|
|
} else {
|
|
set_usb_bt_power(1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void off_reset_power(struct bt_dev_data *pdata, unsigned long down_time)
|
|
{
|
|
if (pdata->gpio_reset > 0) {
|
|
if (pdata->power_on_pin_OD &&
|
|
pdata->power_low_level) {
|
|
gpio_direction_input(pdata->gpio_reset);
|
|
} else {
|
|
gpio_direction_output(pdata->gpio_reset,
|
|
pdata->power_low_level);
|
|
}
|
|
|
|
if (down_time)
|
|
msleep(down_time);
|
|
}
|
|
}
|
|
|
|
static void on_reset_power(struct bt_dev_data *pdata, unsigned long up_time)
|
|
{
|
|
if (pdata->gpio_reset > 0) {
|
|
if (pdata->power_on_pin_OD &&
|
|
!pdata->power_low_level) {
|
|
gpio_direction_input(pdata->gpio_reset);
|
|
} else {
|
|
gpio_direction_output(pdata->gpio_reset,
|
|
!pdata->power_low_level);
|
|
}
|
|
|
|
if (up_time)
|
|
msleep(up_time);
|
|
}
|
|
}
|
|
|
|
static void bt_device_off(struct bt_dev_data *pdata)
|
|
{
|
|
if (sdio_get_vendor() == QCA_ID)
|
|
return;
|
|
|
|
if (pdata->power_down_disable == 0) {
|
|
switch (btpower_evt) {
|
|
case POWER_EVENT_DEF:
|
|
off_def_power(pdata, 0);
|
|
break;
|
|
case POWER_EVENT_RESET:
|
|
off_reset_power(pdata, 0);
|
|
break;
|
|
case POWER_EVENT_EN:
|
|
set_usb_bt_power(0);
|
|
break;
|
|
default:
|
|
pr_err("%s default no electricity", __func__);
|
|
break;
|
|
}
|
|
msleep(20);
|
|
}
|
|
}
|
|
|
|
static void bt_device_init(struct bt_dev_data *pdata)
|
|
{
|
|
btpower_evt = 0;
|
|
btirq_flag = 0;
|
|
|
|
if (pdata->gpio_reset > 0)
|
|
gpio_request(pdata->gpio_reset, BT_RFKILL);
|
|
|
|
if (pdata->gpio_en > 0)
|
|
gpio_request(pdata->gpio_en, BT_RFKILL);
|
|
|
|
if (pdata->gpio_hostwake > 0) {
|
|
gpio_request(pdata->gpio_hostwake, BT_RFKILL);
|
|
gpio_direction_output(pdata->gpio_hostwake, 1);
|
|
}
|
|
|
|
if (pdata->gpio_btwakeup > 0) {
|
|
gpio_request(pdata->gpio_btwakeup, BT_RFKILL);
|
|
gpio_direction_input(pdata->gpio_btwakeup);
|
|
}
|
|
}
|
|
|
|
static void bt_device_deinit(struct bt_dev_data *pdata)
|
|
{
|
|
if (pdata->gpio_reset > 0)
|
|
gpio_free(pdata->gpio_reset);
|
|
|
|
if (pdata->gpio_en > 0)
|
|
gpio_free(pdata->gpio_en);
|
|
|
|
btpower_evt = 0;
|
|
if (pdata->gpio_hostwake > 0)
|
|
gpio_free(pdata->gpio_hostwake);
|
|
}
|
|
|
|
static void bt_device_on(struct bt_dev_data *pdata, unsigned long down_time, unsigned long up_time)
|
|
{
|
|
if (pdata->power_down_disable == 0) {
|
|
switch (btpower_evt) {
|
|
case POWER_EVENT_DEF:
|
|
off_def_power(pdata, down_time);
|
|
on_def_power(pdata, up_time);
|
|
break;
|
|
case POWER_EVENT_RESET:
|
|
off_reset_power(pdata, down_time);
|
|
on_reset_power(pdata, up_time);
|
|
break;
|
|
case POWER_EVENT_EN:
|
|
set_usb_bt_power(0);
|
|
set_usb_bt_power(1);
|
|
break;
|
|
default:
|
|
pr_err("%s default no electricity", __func__);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*The system calls this function when GPIOC_14 interrupt occurs*/
|
|
static irqreturn_t bt_interrupt(int irq, void *dev_id)
|
|
{
|
|
struct bt_dev_data *pdata = (struct bt_dev_data *)dev_id;
|
|
|
|
if (btirq_flag == 1) {
|
|
schedule_work(&pdata->btwakeup_work);
|
|
pr_debug("freeze: test BT IRQ\n");
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static enum hrtimer_restart btwakeup_timer_handler(struct hrtimer *timer)
|
|
{
|
|
struct bt_dev_data *pdata = container_of(timer,
|
|
struct bt_dev_data, timer);
|
|
|
|
if (!gpio_get_value(pdata->gpio_btwakeup) && cnt < 5)
|
|
cnt++;
|
|
if (cnt >= 5 && cnt < 15) {
|
|
if (gpio_get_value(pdata->gpio_btwakeup))
|
|
flag_p++;
|
|
else if (!gpio_get_value(pdata->gpio_btwakeup))
|
|
flag_n++;
|
|
cnt++;
|
|
}
|
|
pr_debug("%s power: %d,netflix:%d\n", __func__, flag_p, flag_n);
|
|
if (flag_p >= 7) {
|
|
pr_info("%s power: %d\n", __func__, flag_p);
|
|
btwake_evt = 2;
|
|
cnt = 0;
|
|
flag_p = 0;
|
|
btirq_flag = 0;
|
|
input_event(pdata->input_dev,
|
|
EV_KEY, KEY_POWER, 1);
|
|
input_sync(pdata->input_dev);
|
|
input_event(pdata->input_dev,
|
|
EV_KEY, KEY_POWER, 0);
|
|
input_sync(pdata->input_dev);
|
|
} else if (flag_n >= 7) {
|
|
pr_info("%s netflix: %d\n", __func__, flag_n);
|
|
btwake_evt = 2;
|
|
cnt = 0;
|
|
flag_n = 0;
|
|
btirq_flag = 0;
|
|
input_event(pdata->input_dev, EV_KEY, 133, 1);
|
|
input_sync(pdata->input_dev);
|
|
input_event(pdata->input_dev, EV_KEY, 133, 0);
|
|
input_sync(pdata->input_dev);
|
|
}
|
|
if (btwake_evt != 2 && cnt != 0)
|
|
hrtimer_start(&pdata->timer,
|
|
ktime_set(0, 20 * 1000000), HRTIMER_MODE_REL);
|
|
return HRTIMER_NORESTART;
|
|
}
|
|
|
|
static void get_btwakeup_irq_work(struct work_struct *work)
|
|
{
|
|
struct bt_dev_data *pdata = container_of(work,
|
|
struct bt_dev_data, btwakeup_work);
|
|
|
|
if (btwake_evt == 2)
|
|
return;
|
|
pr_debug("%s btwake_evt = %d", __func__, btwake_evt);
|
|
hrtimer_start(&pdata->timer,
|
|
ktime_set(0, 100 * 1000000), HRTIMER_MODE_REL);
|
|
}
|
|
|
|
static int bt_set_block(void *data, bool blocked)
|
|
{
|
|
struct bt_dev_data *pdata = data;
|
|
|
|
pr_info("BT_RADIO going: %s\n", blocked ? "off" : "on");
|
|
|
|
if (!blocked) {
|
|
pr_info("AML_BT: going ON,btpower_evt=%d\n", btpower_evt);
|
|
bt_device_on(pdata, 200, 200);
|
|
} else {
|
|
pr_info("AML_BT: going OFF,btpower_evt=%d\n", btpower_evt);
|
|
bt_device_off(pdata);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const struct rfkill_ops bt_rfkill_ops = {
|
|
.set_block = bt_set_block,
|
|
};
|
|
|
|
#if defined(CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND) && defined(CONFIG_AMLOGIC_GX_SUSPEND)
|
|
static void bt_earlysuspend(struct early_suspend *h)
|
|
{
|
|
}
|
|
|
|
static void bt_lateresume(struct early_suspend *h)
|
|
{
|
|
pr_debug("%s,btwake_event=%d\n", __func__, btwake_evt);
|
|
btwake_evt = 2;
|
|
}
|
|
#endif
|
|
|
|
static int bt_suspend(struct platform_device *pdev,
|
|
pm_message_t state)
|
|
{
|
|
struct bt_dev_runtime_data *prdata = platform_get_drvdata(pdev);
|
|
|
|
btwake_evt = 0;
|
|
|
|
pr_info("bt suspend\n");
|
|
disable_irq(prdata->pdata->irqno_wakeup);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bt_resume(struct platform_device *pdev)
|
|
{
|
|
struct bt_dev_runtime_data *prdata = platform_get_drvdata(pdev);
|
|
|
|
pr_info("bt resume\n");
|
|
enable_irq(prdata->pdata->irqno_wakeup);
|
|
btwake_evt = 0;
|
|
|
|
if ((get_resume_method() == RTC_WAKEUP) ||
|
|
(get_resume_method() == AUTO_WAKEUP)) {
|
|
btwake_evt = 1;
|
|
btirq_flag = 1;
|
|
flag_n = 0;
|
|
flag_p = 0;
|
|
cnt = 0;
|
|
}
|
|
if (!distinguish_module() && get_resume_method() == BT_WAKEUP) {
|
|
input_event(prdata->pdata->input_dev,
|
|
EV_KEY, KEY_POWER, 1);
|
|
input_sync(prdata->pdata->input_dev);
|
|
input_event(prdata->pdata->input_dev,
|
|
EV_KEY, KEY_POWER, 0);
|
|
input_sync(prdata->pdata->input_dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bt_probe(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
const void *prop;
|
|
struct rfkill *bt_rfk;
|
|
struct bt_dev_data *pdata = NULL;
|
|
struct bt_dev_runtime_data *prdata;
|
|
struct input_dev *input_dev;
|
|
|
|
#ifdef CONFIG_OF
|
|
if (pdev && pdev->dev.of_node) {
|
|
const char *str;
|
|
//struct gpio_desc *desc;
|
|
|
|
pr_debug("enter %s of_node\n", __func__);
|
|
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
|
|
if (!pdata) {
|
|
ret = -ENOMEM;
|
|
goto err_res;
|
|
}
|
|
|
|
ret = of_property_read_string(pdev->dev.of_node,
|
|
"reset-gpios", &str);
|
|
if (ret) {
|
|
pr_warn("not get gpio_reset\n");
|
|
pdata->gpio_reset = 0;
|
|
} else {
|
|
pdata->gpio_reset = of_get_named_gpio_flags
|
|
(pdev->dev.of_node,
|
|
"reset-gpios", 0, NULL);
|
|
}
|
|
|
|
ret = of_property_read_string(pdev->dev.of_node,
|
|
"bt_en-gpios", &str);
|
|
if (ret) {
|
|
pr_debug("not get gpio_en\n");
|
|
pdata->gpio_en = 0;
|
|
} else {
|
|
pdata->gpio_en = of_get_named_gpio_flags
|
|
(pdev->dev.of_node,
|
|
"bt_en-gpios", 0, NULL);
|
|
}
|
|
ret = of_property_read_string(pdev->dev.of_node,
|
|
"hostwake-gpios", &str);
|
|
if (ret) {
|
|
pr_debug("not get gpio_hostwake\n");
|
|
pdata->gpio_hostwake = 0;
|
|
} else {
|
|
pdata->gpio_hostwake = of_get_named_gpio_flags
|
|
(pdev->dev.of_node,
|
|
"hostwake-gpios",
|
|
0, NULL);
|
|
}
|
|
/*gpio_btwakeup = BT_WAKE_HOST*/
|
|
ret = of_property_read_string(pdev->dev.of_node,
|
|
"btwakeup-gpios", &str);
|
|
if (ret) {
|
|
pr_debug("not get btwakeup-gpios\n");
|
|
pdata->gpio_btwakeup = 0;
|
|
} else {
|
|
pdata->gpio_btwakeup = of_get_named_gpio_flags
|
|
(pdev->dev.of_node,
|
|
"btwakeup-gpios",
|
|
0, NULL);
|
|
}
|
|
|
|
prop = of_get_property(pdev->dev.of_node,
|
|
"power_low_level", NULL);
|
|
if (prop) {
|
|
pr_debug("power on valid level is low");
|
|
pdata->power_low_level = 1;
|
|
} else {
|
|
pr_debug("power on valid level is high");
|
|
pdata->power_low_level = 0;
|
|
pdata->power_on_pin_OD = 0;
|
|
}
|
|
ret = of_property_read_u32(pdev->dev.of_node,
|
|
"power_on_pin_OD",
|
|
&pdata->power_on_pin_OD);
|
|
if (ret)
|
|
pdata->power_on_pin_OD = 0;
|
|
pr_debug("bt: power_on_pin_OD = %d;\n", pdata->power_on_pin_OD);
|
|
ret = of_property_read_u32(pdev->dev.of_node,
|
|
"power_off_flag",
|
|
&pdata->power_off_flag);
|
|
if (ret)
|
|
pdata->power_off_flag = 1;/*bt poweroff*/
|
|
pr_debug("bt: power_off_flag = %d;\n", pdata->power_off_flag);
|
|
|
|
ret = of_property_read_u32(pdev->dev.of_node,
|
|
"power_down_disable",
|
|
&pdata->power_down_disable);
|
|
if (ret)
|
|
pdata->power_down_disable = 0;
|
|
pr_debug("dis power down = %d;\n", pdata->power_down_disable);
|
|
} else if (pdev) {
|
|
pdata = (struct bt_dev_data *)(pdev->dev.platform_data);
|
|
} else {
|
|
ret = -ENOENT;
|
|
goto err_res;
|
|
}
|
|
#else
|
|
pdata = (struct bt_dev_data *)(pdev->dev.platform_data);
|
|
#endif
|
|
bt_addr_class = class_create(THIS_MODULE, "bt_addr");
|
|
ret = class_create_file(bt_addr_class, &class_attr_value);
|
|
|
|
bt_device_init(pdata);
|
|
|
|
if (pdata->power_down_disable == 1) {
|
|
pdata->power_down_disable = 0;
|
|
bt_device_on(pdata, 100, 0);
|
|
pdata->power_down_disable = 1;
|
|
}
|
|
|
|
/* default to bluetooth off */
|
|
/* rfkill_switch_all(RFKILL_TYPE_BLUETOOTH, 1); */
|
|
/* bt_device_off(pdata); */
|
|
|
|
bt_rfk = rfkill_alloc("bt-dev", &pdev->dev,
|
|
RFKILL_TYPE_BLUETOOTH,
|
|
&bt_rfkill_ops, pdata);
|
|
|
|
if (!bt_rfk) {
|
|
pr_debug("rfk alloc fail\n");
|
|
ret = -ENOMEM;
|
|
goto err_rfk_alloc;
|
|
}
|
|
|
|
rfkill_init_sw_state(bt_rfk, true);
|
|
ret = rfkill_register(bt_rfk);
|
|
if (ret) {
|
|
pr_err("rfkill_register fail\n");
|
|
goto err_rfkill;
|
|
}
|
|
prdata = kmalloc(sizeof(*prdata), GFP_KERNEL);
|
|
|
|
if (!prdata)
|
|
goto err_rfkill;
|
|
|
|
prdata->bt_rfk = bt_rfk;
|
|
prdata->pdata = pdata;
|
|
platform_set_drvdata(pdev, prdata);
|
|
#if defined(CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND) && defined(CONFIG_AMLOGIC_GX_SUSPEND)
|
|
bt_early_suspend.level =
|
|
EARLY_SUSPEND_LEVEL_DISABLE_FB;
|
|
bt_early_suspend.suspend = bt_earlysuspend;
|
|
bt_early_suspend.resume = bt_lateresume;
|
|
bt_early_suspend.param = pdev;
|
|
register_early_suspend(&bt_early_suspend);
|
|
#endif
|
|
|
|
/*1.Set BT_WAKE_HOST to the input state;*/
|
|
/*2.Get interrupt number(irqno_wakeup).*/
|
|
pdata->irqno_wakeup = gpio_to_irq(pdata->gpio_btwakeup);
|
|
|
|
/*Register interrupt service function*/
|
|
ret = request_irq(pdata->irqno_wakeup, bt_interrupt,
|
|
IRQF_TRIGGER_FALLING, "bt-irq", (void *)pdata);
|
|
if (ret < 0)
|
|
pr_err("request_irq error ret=%d\n", ret);
|
|
|
|
//disable_irq(pdata->irqno_wakeup);
|
|
|
|
ret = device_init_wakeup(&pdev->dev, 1);
|
|
if (ret)
|
|
pr_err("device_init_wakeup failed: %d\n", ret);
|
|
/*Wake up the interrupt*/
|
|
ret = dev_pm_set_wake_irq(&pdev->dev, pdata->irqno_wakeup);
|
|
if (ret)
|
|
pr_err("dev_pm_set_wake_irq failed: %d\n", ret);
|
|
|
|
INIT_WORK(&pdata->btwakeup_work, get_btwakeup_irq_work);
|
|
|
|
//input
|
|
input_dev = input_allocate_device();
|
|
if (!input_dev) {
|
|
pr_err("[abner test]input_allocate_device failed: %d\n", ret);
|
|
return -EINVAL;
|
|
}
|
|
set_bit(EV_KEY, input_dev->evbit);
|
|
set_bit(KEY_POWER, input_dev->keybit);
|
|
set_bit(133, input_dev->keybit);
|
|
|
|
input_dev->name = "input_btrcu";
|
|
input_dev->phys = "input_btrcu/input0";
|
|
input_dev->dev.parent = &pdev->dev;
|
|
input_dev->id.bustype = BUS_ISA;
|
|
input_dev->id.vendor = 0x0001;
|
|
input_dev->id.product = 0x0001;
|
|
input_dev->id.version = 0x0100;
|
|
input_dev->rep[REP_DELAY] = 0xffffffff;
|
|
input_dev->rep[REP_PERIOD] = 0xffffffff;
|
|
input_dev->keycodesize = sizeof(unsigned short);
|
|
input_dev->keycodemax = 0x1ff;
|
|
ret = input_register_device(input_dev);
|
|
if (ret < 0) {
|
|
pr_err("[abner test]input_register_device failed: %d\n", ret);
|
|
input_free_device(input_dev);
|
|
return -EINVAL;
|
|
}
|
|
pdata->input_dev = input_dev;
|
|
|
|
hrtimer_init(&pdata->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
|
pdata->timer.function = btwakeup_timer_handler;
|
|
|
|
return 0;
|
|
|
|
err_rfkill:
|
|
rfkill_destroy(bt_rfk);
|
|
err_rfk_alloc:
|
|
bt_device_deinit(pdata);
|
|
err_res:
|
|
return ret;
|
|
}
|
|
|
|
static int bt_remove(struct platform_device *pdev)
|
|
{
|
|
struct bt_dev_runtime_data *prdata =
|
|
platform_get_drvdata(pdev);
|
|
struct rfkill *rfk = NULL;
|
|
struct bt_dev_data *pdata = NULL;
|
|
|
|
platform_set_drvdata(pdev, NULL);
|
|
|
|
if (prdata) {
|
|
rfk = prdata->bt_rfk;
|
|
pdata = prdata->pdata;
|
|
}
|
|
|
|
if (pdata) {
|
|
bt_device_deinit(pdata);
|
|
kfree(pdata);
|
|
}
|
|
|
|
if (rfk) {
|
|
rfkill_unregister(rfk);
|
|
rfkill_destroy(rfk);
|
|
}
|
|
rfk = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id bt_dev_dt_match[] = {
|
|
{ .compatible = "amlogic, aml-bt",
|
|
},
|
|
{},
|
|
};
|
|
#else
|
|
#define bt_dev_dt_match NULL
|
|
#endif
|
|
|
|
static struct platform_driver bt_driver = {
|
|
.driver = {
|
|
.name = "aml_bt",
|
|
.of_match_table = bt_dev_dt_match,
|
|
},
|
|
.probe = bt_probe,
|
|
.remove = bt_remove,
|
|
.suspend = bt_suspend,
|
|
.resume = bt_resume,
|
|
};
|
|
|
|
int __init bt_init(void)
|
|
{
|
|
pr_info("amlogic rfkill init\n");
|
|
|
|
return platform_driver_register(&bt_driver);
|
|
}
|
|
|
|
void __exit bt_exit(void)
|
|
{
|
|
platform_driver_unregister(&bt_driver);
|
|
}
|
|
|
|
module_param(btpower_evt, int, 0664);
|
|
MODULE_PARM_DESC(btpower_evt, "btpower_evt");
|
|
|
|
module_param(btwake_evt, int, 0664);
|
|
MODULE_PARM_DESC(btwake_evt, "btwake_evt");
|
|
|
|
/**************** bt mac *****************/
|
|
|
|
//static int __init mac_addr_set(char *line)
|
|
static int mac_addr_set(char *line)
|
|
{
|
|
if (line) {
|
|
pr_debug("try to read bt mac from emmc key!\n");
|
|
strncpy(bt_addr, line, sizeof(bt_addr) - 1);
|
|
bt_addr[sizeof(bt_addr) - 1] = '\0';
|
|
btmac = (char *)bt_addr;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
__setup("mac_bt=", mac_addr_set);
|
|
|