From 19ab80fe27aa05babef33b7d781e2e2cce72cf58 Mon Sep 17 00:00:00 2001 From: Zou Dengming Date: Wed, 19 Apr 2023 20:47:32 +0800 Subject: [PATCH] net: rfkill-bt: add power up func when waked by bt-irq only. when rtk(also some others) wifi/bt combo chips suspend, it disconnect from bt-remote control unit(RCU). when RCU's powerkey pressed, it will send ble broadcast to rtk bt chip. As the rtk chips receive the broadcast, it should wake up the host, but it can't send bt-hid powerkey to host because it had lost communication with host. So,it just send a IRQ pulse to host. The host should resume from suspend. Because the RCU just send a IRQ pulse, so rk chips just resume by IRQ and then go to sleep again for no power-key event. this is not expected. we expect to power up system wholly. With this path, we try to power up the system. so we add a power-up key event when BT IRQ received. We also can control the "/proc/bluetooth/sleep/powerupkey" node to enable or disable this function. Change-Id: Ie59b4a2c4cd2f91820d31835df86565003126465 Signed-off-by: Zou Dengming --- net/rfkill/Kconfig | 1 + net/rfkill/rfkill-bt.c | 137 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 127 insertions(+), 11 deletions(-) diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index 28c19d8dd579..1b12af4d009d 100644 --- a/net/rfkill/Kconfig +++ b/net/rfkill/Kconfig @@ -35,6 +35,7 @@ config RFKILL_GPIO config RFKILL_RK tristate "Rockchip RFKILL driver" + select INPUT depends on RFKILL depends on MMC depends on ARCH_ROCKCHIP diff --git a/net/rfkill/rfkill-bt.c b/net/rfkill/rfkill-bt.c index 73b802c7faa4..e76de540804c 100644 --- a/net/rfkill/rfkill-bt.c +++ b/net/rfkill/rfkill-bt.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -69,9 +70,11 @@ struct rfkill_rk_data { struct wake_lock bt_irq_wl; struct delayed_work bt_sleep_delay_work; int irq_req; + bool enable_power_key; }; static struct rfkill_rk_data *g_rfkill = NULL; +static struct input_dev *power_key_dev; static const char bt_name[] = #if defined(CONFIG_BCM4330) @@ -113,6 +116,20 @@ static const char bt_name[] = #endif ; +static int rfkill_rk_power_key_up(void) +{ + if (!power_key_dev) + return -ENODEV; + + input_report_key(power_key_dev, KEY_POWER, 1); + input_sync(power_key_dev); + msleep(20); + input_report_key(power_key_dev, KEY_POWER, 0); + input_sync(power_key_dev); + + return 0; +} + static irqreturn_t rfkill_rk_wake_host_irq(int irq, void *dev) { struct rfkill_rk_data *rfkill = dev; @@ -124,6 +141,16 @@ static irqreturn_t rfkill_rk_wake_host_irq(int irq, void *dev) wake_lock_timeout(&rfkill->bt_irq_wl, msecs_to_jiffies(BT_IRQ_WAKELOCK_TIMEOUT)); + if (rfkill->enable_power_key) + return IRQ_WAKE_THREAD; + + return IRQ_HANDLED; +} + +static irqreturn_t rfkill_rk_wake_host_irq_thread(int irq, void *dev) +{ + rfkill_rk_power_key_up(); + return IRQ_HANDLED; } @@ -165,11 +192,12 @@ static int rfkill_rk_setup_wake_irq(struct rfkill_rk_data *rfkill, int flag) LOG("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->gpio.enable == GPIO_ACTIVE_LOW) ? - IRQF_TRIGGER_FALLING : - IRQF_TRIGGER_RISING, - irq->name, rfkill); + ret = request_threaded_irq(irq->irq, rfkill_rk_wake_host_irq, + rfkill_rk_wake_host_irq_thread, + IRQF_ONESHOT | ((irq->gpio.enable == GPIO_ACTIVE_LOW) ? + IRQF_TRIGGER_FALLING : + IRQF_TRIGGER_RISING), + irq->name, rfkill); if (ret) goto fail2; rfkill->irq_req = 1; @@ -488,7 +516,6 @@ static ssize_t bluesleep_write_proc_btwrite(struct file *file, return -EFAULT; DBG("btwrite %c\n", b); - /* HCI_DEV_WRITE */ if (b != '0') rfkill_rk_sleep_bt(BT_WAKEUP); else @@ -497,6 +524,52 @@ static ssize_t bluesleep_write_proc_btwrite(struct file *file, return count; } +static ssize_t bluesleep_read_proc_powerupkey(struct file *file, + char __user *buffer, size_t count, + loff_t *data) +{ + struct rfkill_rk_data *rfkill = g_rfkill; + char src[2]; + + if (*data >= 1) + return 0; + + if (!rfkill) + return -EFAULT; + + src[0] = rfkill->enable_power_key ? '1' : '0'; + src[1] = '\n'; + if (copy_to_user(buffer, src, 2)) + return -EFAULT; + *data = 1; + + return 2; +} + +static ssize_t bluesleep_write_proc_powerupkey(struct file *file, + const char __user *buffer, + size_t count, loff_t *data) +{ + char b; + struct rfkill_rk_data *rfkill = g_rfkill; + + if (!rfkill) + return -EFAULT; + + if (count < 1) + return -EINVAL; + + if (copy_from_user(&b, buffer, 1)) + return -EFAULT; + + if (b != '0') + rfkill->enable_power_key = true; + else + rfkill->enable_power_key = false; + + return count; +} + #ifdef CONFIG_OF static int bluetooth_platdata_parse_dt(struct device *dev, struct rfkill_rk_platform_data *data) @@ -594,6 +667,37 @@ static const struct proc_ops bluesleep_btwrite = { .proc_write = bluesleep_write_proc_btwrite, }; +static const struct proc_ops bluesleep_powerupkey = { + .proc_read = bluesleep_read_proc_powerupkey, + .proc_write = bluesleep_write_proc_powerupkey, +}; + +static int rfkill_rk_register_power_key(struct device *dev) +{ + int ret = 0; + + /* register input device */ + power_key_dev = devm_input_allocate_device(dev); + if (!power_key_dev) { + LOG("%s: not enough memory for input device\n", __func__); + return -ENOMEM; + } + + power_key_dev->name = "bt-powerkey"; + power_key_dev->id.bustype = BUS_HOST; + + power_key_dev->evbit[0] = BIT_MASK(EV_KEY); + set_bit(KEY_POWER, power_key_dev->keybit); + + ret = input_register_device(power_key_dev); + if (ret) { + LOG("%s: register input device exception, exit\n", __func__); + return -EBUSY; + } + + return ret; +} + static int rfkill_rk_probe(struct platform_device *pdev) { struct rfkill_rk_data *rfkill; @@ -660,6 +764,14 @@ static int rfkill_rk_probe(struct platform_device *pdev) goto fail_alloc; } + /* read/write proc entries */ + ent = proc_create("powerupkey", 0444, sleep_dir, &bluesleep_powerupkey); + if (!ent) { + LOG("Unable to create /proc/%s/powerupkey entry", PROC_DIR); + ret = -ENOMEM; + goto fail_alloc; + } + DBG("init gpio\n"); ret = rfkill_rk_setup_gpio(pdev, &pdata->poweron_gpio, pdata->name, @@ -692,8 +804,10 @@ static int rfkill_rk_probe(struct platform_device *pdev) DBG("setup rfkill\n"); rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type, &rfkill_rk_ops, rfkill); - if (!rfkill->rfkill_dev) + if (!rfkill->rfkill_dev) { + ret = -ENOMEM; goto fail_alloc; + } rfkill_init_sw_state(rfkill->rfkill_dev, BT_BLOCKED); rfkill_set_sw_state(rfkill->rfkill_dev, BT_BLOCKED); @@ -720,18 +834,18 @@ static int rfkill_rk_probe(struct platform_device *pdev) LOG("%s device registered.\n", pdata->name); + if (rfkill_rk_register_power_key(&pdev->dev) != 0) + goto fail_rfkill; + return 0; fail_rfkill: rfkill_destroy(rfkill->rfkill_dev); fail_alloc: - - remove_proc_entry("btwrite", sleep_dir); - remove_proc_entry("lpm", sleep_dir); + remove_proc_subtree("bluetooth/sleep", NULL); fail_setup_wake_irq: wake_lock_destroy(&rfkill->bt_irq_wl); fail_gpio: - g_rfkill = NULL; return ret; } @@ -744,6 +858,7 @@ static int rfkill_rk_remove(struct platform_device *pdev) rfkill_unregister(rfkill->rfkill_dev); rfkill_destroy(rfkill->rfkill_dev); + remove_proc_subtree("bluetooth/sleep", NULL); cancel_delayed_work_sync(&rfkill->bt_sleep_delay_work);