Received: by 2002:a25:4158:0:0:0:0:0 with SMTP id o85csp343779yba; Thu, 18 Apr 2019 02:09:57 -0700 (PDT) X-Google-Smtp-Source: APXvYqyIdMVdM/nwwE7mrUrekPS+M4YfVqIRHbWQT/2XNY1q5vjDMZZyO9H06vXAer+/5Nsg3wLA X-Received: by 2002:a62:ab13:: with SMTP id p19mr96405791pff.131.1555578597397; Thu, 18 Apr 2019 02:09:57 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1555578597; cv=none; d=google.com; s=arc-20160816; b=ayAb0Ci/muT8Y6EBAXEVDBUGzUSQJdbDQmyCFbE6dpe300hx9Fb5Kio5fin4oMqHce vY+v7fXN5mjE7OM3/1izWB+G2yZG9uyj9vuWqZHQ3ktb9cUVUrxc1W+hI9H01gZ/lOlz NUxweyOn2MOaXgJEr/tmhNyfUpQ/KgN/K5HpdJmi5nSBqa23pzrHmShQWVFaWItQE+jU LySF4CMvrZnQ2RysrtdUozw+CQkSLW8W8MnFgihNMq7ngq6vXqAOwhvm3F+ygV0LhNRD X6sU9WT9LYqbJHl9NnV5qcizv7sIC3vAjNBGSD4aLH3pO/bLLNMDcohoJgzuxCK84a5Z UU7w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from; bh=bATbk37OMoDAicswf2gj41MM90MdIjJkozfDnzLok9A=; b=1F5jT3yurCqvg3ft8sY5N4TMNBgupBIBnP8zY9GXFOTGQ0nXnC0oHeXv3ed45d6USz bIWFbvJt9VldSzj4NiO4QbkbHM1R6DstwxESWCT7uYbEIVEicSWgeZpRxKGk1a6fHdYK TWRXlVh6PfC1Jc1i4JUOTNL+p9mjbHN4KmUrLyC1ULl9XTCRBeliwZoNj+UCGEV4W/sk MhIgKK5tg53v31aSFnyP2QG/csslJBO97GB8aP73EfupM0nAoF+pxqhH50Tjw9eFJohz ICfSf4MhKlTIb+kQ0aCBJt4cpryDdeBesB1E+dtbm9Sj5AQPUtiPywRMsXVj2bpLVPHK yDsg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id f124si1347524pgc.596.2019.04.18.02.09.40; Thu, 18 Apr 2019 02:09:57 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388383AbfDRJIX (ORCPT + 99 others); Thu, 18 Apr 2019 05:08:23 -0400 Received: from mailgw02.mediatek.com ([210.61.82.184]:60465 "EHLO mailgw02.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S2388709AbfDRJIQ (ORCPT ); Thu, 18 Apr 2019 05:08:16 -0400 X-UUID: 88763945fa9240349013fd3dcd49afb3-20190418 X-UUID: 88763945fa9240349013fd3dcd49afb3-20190418 Received: from mtkcas09.mediatek.inc [(172.21.101.178)] by mailgw02.mediatek.com (envelope-from ) (mhqrelay.mediatek.com ESMTP with TLS) with ESMTP id 2045535816; Thu, 18 Apr 2019 17:08:06 +0800 Received: from mtkcas07.mediatek.inc (172.21.101.84) by mtkmbs01n2.mediatek.inc (172.21.101.79) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 18 Apr 2019 17:08:04 +0800 Received: from mtkswgap22.mediatek.inc (172.21.77.33) by mtkcas07.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Thu, 18 Apr 2019 17:08:04 +0800 From: To: , CC: , , , , , Sean Wang Subject: [PATCH v1 4/4] Bluetooth: btmtksdio: Add runtime PM support to SDIO based Bluetooth Date: Thu, 18 Apr 2019 17:08:02 +0800 Message-ID: <3946c88c48f088c242d94204d7302364133fe2b9.1555577565.git.sean.wang@mediatek.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain X-TM-SNTS-SMTP: 1E24946ECF3F6A7B0B2D9190D01471AB1373641E07AC3C5C91675AD5886222B72000:8 X-MTK: N Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Sean Wang Add runtime PM support to btmtksdio. With this way, there will be the benefit of the device entering the more power saving state once it is been a while data traffic is idle. Signed-off-by: Sean Wang --- drivers/bluetooth/btmtksdio.c | 144 ++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c index 877c0a831775..813338288453 100644 --- a/drivers/bluetooth/btmtksdio.c +++ b/drivers/bluetooth/btmtksdio.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,10 @@ #define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin" #define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin" +#define MTKBTSDIO_AUTOSUSPEND_DELAY 8000 + +static bool enable_autosuspend; + struct btmtksdio_data { const char *fwname; }; @@ -150,6 +155,7 @@ struct btmtk_hci_wmt_params { struct btmtksdio_dev { struct hci_dev *hdev; struct sdio_func *func; + struct device *dev; struct work_struct tx_work; unsigned long tx_state; @@ -301,6 +307,8 @@ static void btmtksdio_tx_work(struct work_struct *work) struct sk_buff *skb; int err; + pm_runtime_get_sync(bdev->dev); + sdio_claim_host(bdev->func); while ((skb = skb_dequeue(&bdev->txq))) { @@ -313,6 +321,9 @@ static void btmtksdio_tx_work(struct work_struct *work) } sdio_release_host(bdev->func); + + pm_runtime_mark_last_busy(bdev->dev); + pm_runtime_put_autosuspend(bdev->dev); } static int btmtksdio_recv_event(struct hci_dev *hdev, struct sk_buff *skb) @@ -471,6 +482,18 @@ static void btmtksdio_interrupt(struct sdio_func *func) u32 int_status; u16 rx_size; + /* It is required that the host gets ownership from the device before + * accessing any register, however, if SDIO host is not being released, + * a potential deadlock probably happens in a circular wait between SDIO + * IRQ work and PM runtime work. So, we have to explicitly release SDIO + * host here and claim again after the PM runtime work is all done. + */ + sdio_release_host(bdev->func); + + pm_runtime_get_sync(bdev->dev); + + sdio_claim_host(bdev->func); + /* Disable interrupt */ sdio_writel(func, C_INT_EN_CLR, MTK_REG_CHLPCR, 0); @@ -507,6 +530,9 @@ static void btmtksdio_interrupt(struct sdio_func *func) /* Enable interrupt */ sdio_writel(func, C_INT_EN_SET, MTK_REG_CHLPCR, 0); + + pm_runtime_mark_last_busy(bdev->dev); + pm_runtime_put_autosuspend(bdev->dev); } static int btmtksdio_open(struct hci_dev *hdev) @@ -815,6 +841,23 @@ static int btmtksdio_setup(struct hci_dev *hdev) delta = ktime_sub(rettime, calltime); duration = (unsigned long long)ktime_to_ns(delta) >> 10; + pm_runtime_set_autosuspend_delay(bdev->dev, + MTKBTSDIO_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(bdev->dev); + + err = pm_runtime_set_active(bdev->dev); + if (err < 0) + return err; + + /* Default forbid runtime auto suspend, that can be allowed by + * enable_autosuspend flag or the PM runtime entry under sysfs. + */ + pm_runtime_forbid(bdev->dev); + pm_runtime_enable(bdev->dev); + + if (enable_autosuspend) + pm_runtime_allow(bdev->dev); + bt_dev_info(hdev, "Device setup in %llu usecs", duration); return 0; @@ -822,10 +865,16 @@ static int btmtksdio_setup(struct hci_dev *hdev) static int btmtksdio_shutdown(struct hci_dev *hdev) { + struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); struct btmtk_hci_wmt_params wmt_params; u8 param = 0x0; int err; + /* Get back the state to be consistent with the state + * in btmtksdio_setup. + */ + pm_runtime_get_sync(bdev->dev); + /* Disable the device */ wmt_params.op = MTK_WMT_FUNC_CTRL; wmt_params.flag = 0; @@ -839,6 +888,9 @@ static int btmtksdio_shutdown(struct hci_dev *hdev) return err; } + pm_runtime_put_noidle(bdev->dev); + pm_runtime_disable(bdev->dev); + return 0; } @@ -885,6 +937,7 @@ static int btmtksdio_probe(struct sdio_func *func, if (!bdev->data) return -ENODEV; + bdev->dev = &func->dev; bdev->func = func; INIT_WORK(&bdev->tx_work, btmtksdio_tx_work); @@ -922,6 +975,25 @@ static int btmtksdio_probe(struct sdio_func *func, sdio_set_drvdata(func, bdev); + /* pm_runtime_enable would be done after the firmware is being + * downloaded because the core layer probably already enables + * runtime PM for this func such as the case host->caps & + * MMC_CAP_POWER_OFF_CARD. + */ + if (pm_runtime_enabled(bdev->dev)) + pm_runtime_disable(bdev->dev); + + /* As explaination in drivers/mmc/core/sdio_bus.c tells us: + * Unbound SDIO functions are always suspended. + * During probe, the function is set active and the usage count + * is incremented. If the driver supports runtime PM, + * it should call pm_runtime_put_noidle() in its probe routine and + * pm_runtime_get_noresume() in its remove routine. + * + * So, put a pm_runtime_put_noidle here ! + */ + pm_runtime_put_noidle(bdev->dev); + return 0; } @@ -933,6 +1005,9 @@ static void btmtksdio_remove(struct sdio_func *func) if (!bdev) return; + /* Be consistent the state in btmtksdio_probe */ + pm_runtime_get_noresume(bdev->dev); + hdev = bdev->hdev; sdio_set_drvdata(func, NULL); @@ -940,15 +1015,84 @@ static void btmtksdio_remove(struct sdio_func *func) hci_free_dev(hdev); } +#ifdef CONFIG_PM +static int btmtksdio_runtime_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct btmtksdio_dev *bdev; + u32 status; + int err; + + bdev = sdio_get_drvdata(func); + if (!bdev) + return 0; + + sdio_claim_host(bdev->func); + + sdio_writel(bdev->func, C_FW_OWN_REQ_SET, MTK_REG_CHLPCR, &err); + if (err < 0) + goto out; + + err = readx_poll_timeout(btmtksdio_drv_own_query, bdev, status, + !(status & C_COM_DRV_OWN), 2000, 1000000); +out: + bt_dev_info(bdev->hdev, "status (%d) return ownership to device", err); + + sdio_release_host(bdev->func); + + return err; +} + +static int btmtksdio_runtime_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct btmtksdio_dev *bdev; + u32 status; + int err; + + bdev = sdio_get_drvdata(func); + if (!bdev) + return 0; + + sdio_claim_host(bdev->func); + + sdio_writel(bdev->func, C_FW_OWN_REQ_CLR, MTK_REG_CHLPCR, &err); + if (err < 0) + goto out; + + err = readx_poll_timeout(btmtksdio_drv_own_query, bdev, status, + status & C_COM_DRV_OWN, 2000, 1000000); +out: + bt_dev_info(bdev->hdev, "status (%d) get ownership from device", err); + + sdio_release_host(bdev->func); + + return err; +} + +static UNIVERSAL_DEV_PM_OPS(btmtksdio_pm_ops, btmtksdio_runtime_suspend, + btmtksdio_runtime_resume, NULL); +#define BTMTKSDIO_PM_OPS (&btmtksdio_pm_ops) +#else /* CONFIG_PM */ +#define BTMTKSDIO_PM_OPS NULL +#endif /* CONFIG_PM */ + static struct sdio_driver btmtksdio_driver = { .name = "btmtksdio", .probe = btmtksdio_probe, .remove = btmtksdio_remove, .id_table = btmtksdio_table, + .drv = { + .owner = THIS_MODULE, + .pm = BTMTKSDIO_PM_OPS, + } }; module_sdio_driver(btmtksdio_driver); +module_param(enable_autosuspend, bool, 0644); +MODULE_PARM_DESC(enable_autosuspend, "Enable autosuspend by default"); + MODULE_AUTHOR("Sean Wang "); MODULE_DESCRIPTION("MediaTek Bluetooth SDIO driver ver " VERSION); MODULE_VERSION(VERSION); -- 2.18.0