Skip to content

Commit 6912aaf

Browse files
EvenxfJiri Kosina
authored andcommitted
HID: intel-thc-hid: intel-quickspi: Add PM implementation
Implement THC QuickSPI driver power management callbacks. Co-developed-by: Xinpeng Sun <xinpeng.sun@intel.com> Signed-off-by: Xinpeng Sun <xinpeng.sun@intel.com> Signed-off-by: Even Xu <even.xu@intel.com> Tested-by: Rui Zhang <rui1.zhang@intel.com> Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca> Reviewed-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca> Tested-by: Aaron Ma <aaron.ma@canonical.com> Signed-off-by: Jiri Kosina <jkosina@suse.com>
1 parent 4138f21 commit 6912aaf

File tree

3 files changed

+262
-0
lines changed

3 files changed

+262
-0
lines changed

drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/interrupt.h>
1010
#include <linux/irqreturn.h>
1111
#include <linux/pci.h>
12+
#include <linux/pm_runtime.h>
1213

1314
#include "intel-thc-dev.h"
1415
#include "intel-thc-hw.h"
@@ -281,6 +282,10 @@ static irqreturn_t quickspi_irq_thread_handler(int irq, void *dev_id)
281282
if (qsdev->state == QUICKSPI_DISABLED)
282283
return IRQ_HANDLED;
283284

285+
ret = pm_runtime_resume_and_get(qsdev->dev);
286+
if (ret)
287+
return IRQ_HANDLED;
288+
284289
int_mask = thc_interrupt_handler(qsdev->thc_hw);
285290

286291
if (int_mask & BIT(THC_FATAL_ERR_INT) || int_mask & BIT(THC_TXN_ERR_INT)) {
@@ -318,6 +323,9 @@ static irqreturn_t quickspi_irq_thread_handler(int irq, void *dev_id)
318323
if (try_recover(qsdev))
319324
qsdev->state = QUICKSPI_DISABLED;
320325

326+
pm_runtime_mark_last_busy(qsdev->dev);
327+
pm_runtime_put_autosuspend(qsdev->dev);
328+
321329
return IRQ_HANDLED;
322330
}
323331

@@ -645,6 +653,13 @@ static int quickspi_probe(struct pci_dev *pdev,
645653

646654
qsdev->state = QUICKSPI_ENABLED;
647655

656+
/* Enable runtime power management */
657+
pm_runtime_use_autosuspend(qsdev->dev);
658+
pm_runtime_set_autosuspend_delay(qsdev->dev, DEFAULT_AUTO_SUSPEND_DELAY_MS);
659+
pm_runtime_mark_last_busy(qsdev->dev);
660+
pm_runtime_put_noidle(qsdev->dev);
661+
pm_runtime_put_autosuspend(qsdev->dev);
662+
648663
dev_dbg(&pdev->dev, "QuickSPI probe success\n");
649664

650665
return 0;
@@ -680,6 +695,8 @@ static void quickspi_remove(struct pci_dev *pdev)
680695
quickspi_hid_remove(qsdev);
681696
quickspi_dma_deinit(qsdev);
682697

698+
pm_runtime_get_noresume(qsdev->dev);
699+
683700
quickspi_dev_deinit(qsdev);
684701

685702
pcim_iounmap_regions(pdev, BIT(0));
@@ -709,6 +726,234 @@ static void quickspi_shutdown(struct pci_dev *pdev)
709726
quickspi_dev_deinit(qsdev);
710727
}
711728

729+
static int quickspi_suspend(struct device *device)
730+
{
731+
struct pci_dev *pdev = to_pci_dev(device);
732+
struct quickspi_device *qsdev;
733+
int ret;
734+
735+
qsdev = pci_get_drvdata(pdev);
736+
if (!qsdev)
737+
return -ENODEV;
738+
739+
ret = quickspi_set_power(qsdev, HIDSPI_SLEEP);
740+
if (ret)
741+
return ret;
742+
743+
ret = thc_interrupt_quiesce(qsdev->thc_hw, true);
744+
if (ret)
745+
return ret;
746+
747+
thc_interrupt_enable(qsdev->thc_hw, false);
748+
749+
thc_dma_unconfigure(qsdev->thc_hw);
750+
751+
return 0;
752+
}
753+
754+
static int quickspi_resume(struct device *device)
755+
{
756+
struct pci_dev *pdev = to_pci_dev(device);
757+
struct quickspi_device *qsdev;
758+
int ret;
759+
760+
qsdev = pci_get_drvdata(pdev);
761+
if (!qsdev)
762+
return -ENODEV;
763+
764+
ret = thc_port_select(qsdev->thc_hw, THC_PORT_TYPE_SPI);
765+
if (ret)
766+
return ret;
767+
768+
thc_interrupt_config(qsdev->thc_hw);
769+
770+
thc_interrupt_enable(qsdev->thc_hw, true);
771+
772+
ret = thc_dma_configure(qsdev->thc_hw);
773+
if (ret)
774+
return ret;
775+
776+
ret = thc_interrupt_quiesce(qsdev->thc_hw, false);
777+
if (ret)
778+
return ret;
779+
780+
ret = quickspi_set_power(qsdev, HIDSPI_ON);
781+
if (ret)
782+
return ret;
783+
784+
return 0;
785+
}
786+
787+
static int quickspi_freeze(struct device *device)
788+
{
789+
struct pci_dev *pdev = to_pci_dev(device);
790+
struct quickspi_device *qsdev;
791+
int ret;
792+
793+
qsdev = pci_get_drvdata(pdev);
794+
if (!qsdev)
795+
return -ENODEV;
796+
797+
ret = thc_interrupt_quiesce(qsdev->thc_hw, true);
798+
if (ret)
799+
return ret;
800+
801+
thc_interrupt_enable(qsdev->thc_hw, false);
802+
803+
thc_dma_unconfigure(qsdev->thc_hw);
804+
805+
return 0;
806+
}
807+
808+
static int quickspi_thaw(struct device *device)
809+
{
810+
struct pci_dev *pdev = to_pci_dev(device);
811+
struct quickspi_device *qsdev;
812+
int ret;
813+
814+
qsdev = pci_get_drvdata(pdev);
815+
if (!qsdev)
816+
return -ENODEV;
817+
818+
ret = thc_dma_configure(qsdev->thc_hw);
819+
if (ret)
820+
return ret;
821+
822+
thc_interrupt_enable(qsdev->thc_hw, true);
823+
824+
ret = thc_interrupt_quiesce(qsdev->thc_hw, false);
825+
if (ret)
826+
return ret;
827+
828+
return 0;
829+
}
830+
831+
static int quickspi_poweroff(struct device *device)
832+
{
833+
struct pci_dev *pdev = to_pci_dev(device);
834+
struct quickspi_device *qsdev;
835+
int ret;
836+
837+
qsdev = pci_get_drvdata(pdev);
838+
if (!qsdev)
839+
return -ENODEV;
840+
841+
ret = thc_interrupt_quiesce(qsdev->thc_hw, true);
842+
if (ret)
843+
return ret;
844+
845+
thc_interrupt_enable(qsdev->thc_hw, false);
846+
847+
thc_ltr_unconfig(qsdev->thc_hw);
848+
849+
quickspi_dma_deinit(qsdev);
850+
851+
return 0;
852+
}
853+
854+
static int quickspi_restore(struct device *device)
855+
{
856+
struct pci_dev *pdev = to_pci_dev(device);
857+
struct quickspi_device *qsdev;
858+
int ret;
859+
860+
qsdev = pci_get_drvdata(pdev);
861+
if (!qsdev)
862+
return -ENODEV;
863+
864+
ret = thc_interrupt_quiesce(qsdev->thc_hw, true);
865+
if (ret)
866+
return ret;
867+
868+
/* Reconfig THC HW when back from hibernate */
869+
ret = thc_port_select(qsdev->thc_hw, THC_PORT_TYPE_SPI);
870+
if (ret)
871+
return ret;
872+
873+
thc_spi_input_output_address_config(qsdev->thc_hw,
874+
qsdev->input_report_hdr_addr,
875+
qsdev->input_report_bdy_addr,
876+
qsdev->output_report_addr);
877+
878+
ret = thc_spi_read_config(qsdev->thc_hw, qsdev->spi_freq_val,
879+
qsdev->spi_read_io_mode,
880+
qsdev->spi_read_opcode,
881+
qsdev->spi_packet_size);
882+
if (ret)
883+
return ret;
884+
885+
ret = thc_spi_write_config(qsdev->thc_hw, qsdev->spi_freq_val,
886+
qsdev->spi_write_io_mode,
887+
qsdev->spi_write_opcode,
888+
qsdev->spi_packet_size,
889+
qsdev->performance_limit);
890+
if (ret)
891+
return ret;
892+
893+
thc_interrupt_config(qsdev->thc_hw);
894+
895+
thc_interrupt_enable(qsdev->thc_hw, true);
896+
897+
/* TIC may lose power, needs go through reset flow */
898+
ret = reset_tic(qsdev);
899+
if (ret)
900+
return ret;
901+
902+
ret = thc_dma_configure(qsdev->thc_hw);
903+
if (ret)
904+
return ret;
905+
906+
thc_ltr_config(qsdev->thc_hw,
907+
qsdev->active_ltr_val,
908+
qsdev->low_power_ltr_val);
909+
910+
thc_change_ltr_mode(qsdev->thc_hw, THC_LTR_MODE_ACTIVE);
911+
912+
return 0;
913+
}
914+
915+
static int quickspi_runtime_suspend(struct device *device)
916+
{
917+
struct pci_dev *pdev = to_pci_dev(device);
918+
struct quickspi_device *qsdev;
919+
920+
qsdev = pci_get_drvdata(pdev);
921+
if (!qsdev)
922+
return -ENODEV;
923+
924+
thc_change_ltr_mode(qsdev->thc_hw, THC_LTR_MODE_LP);
925+
926+
pci_save_state(pdev);
927+
928+
return 0;
929+
}
930+
931+
static int quickspi_runtime_resume(struct device *device)
932+
{
933+
struct pci_dev *pdev = to_pci_dev(device);
934+
struct quickspi_device *qsdev;
935+
936+
qsdev = pci_get_drvdata(pdev);
937+
if (!qsdev)
938+
return -ENODEV;
939+
940+
thc_change_ltr_mode(qsdev->thc_hw, THC_LTR_MODE_ACTIVE);
941+
942+
return 0;
943+
}
944+
945+
static const struct dev_pm_ops quickspi_pm_ops = {
946+
.suspend = quickspi_suspend,
947+
.resume = quickspi_resume,
948+
.freeze = quickspi_freeze,
949+
.thaw = quickspi_thaw,
950+
.poweroff = quickspi_poweroff,
951+
.restore = quickspi_restore,
952+
.runtime_suspend = quickspi_runtime_suspend,
953+
.runtime_resume = quickspi_runtime_resume,
954+
.runtime_idle = NULL,
955+
};
956+
712957
static const struct pci_device_id quickspi_pci_tbl[] = {
713958
{PCI_DEVICE_DATA(INTEL, THC_MTL_DEVICE_ID_SPI_PORT1, &mtl), },
714959
{PCI_DEVICE_DATA(INTEL, THC_MTL_DEVICE_ID_SPI_PORT2, &mtl), },
@@ -728,6 +973,7 @@ static struct pci_driver quickspi_driver = {
728973
.probe = quickspi_probe,
729974
.remove = quickspi_remove,
730975
.shutdown = quickspi_shutdown,
976+
.driver.pm = &quickspi_pm_ops,
731977
.driver.probe_type = PROBE_PREFER_ASYNCHRONOUS,
732978
};
733979

drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@
4747
#define MAX_PACKET_SIZE_VALUE_MTL 128
4848
#define MAX_PACKET_SIZE_VALUE_LNL 256
4949

50+
/*
51+
* THC uses runtime auto suspend to dynamically switch between THC active LTR
52+
* and low power LTR to save CPU power.
53+
* Default value is 5000ms, that means if no touch event in this time, THC will
54+
* change to low power LTR mode.
55+
*/
56+
#define DEFAULT_AUTO_SUSPEND_DELAY_MS 5000
57+
5058
enum quickspi_dev_state {
5159
QUICKSPI_NONE,
5260
QUICKSPI_RESETING,

drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <linux/hid.h>
55
#include <linux/input.h>
6+
#include <linux/pm_runtime.h>
67

78
#include "quickspi-dev.h"
89
#include "quickspi-hid.h"
@@ -54,6 +55,10 @@ static int quickspi_hid_raw_request(struct hid_device *hid,
5455
struct quickspi_device *qsdev = hid->driver_data;
5556
int ret = 0;
5657

58+
ret = pm_runtime_resume_and_get(qsdev->dev);
59+
if (ret)
60+
return ret;
61+
5762
switch (reqtype) {
5863
case HID_REQ_GET_REPORT:
5964
ret = quickspi_get_report(qsdev, rtype, reportnum, buf);
@@ -66,6 +71,9 @@ static int quickspi_hid_raw_request(struct hid_device *hid,
6671
break;
6772
}
6873

74+
pm_runtime_mark_last_busy(qsdev->dev);
75+
pm_runtime_put_autosuspend(qsdev->dev);
76+
6977
return ret;
7078
}
7179

0 commit comments

Comments
 (0)