Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

i3c: mipi-i3c-hci: Add optional Runtime PM support

Implement optional Runtime PM support for the MIPI I3C HCI driver.
Introduce runtime suspend and resume callbacks to manage bus state and
restore hardware configuration after resume. Optionally enable autosuspend
with a default delay of 1 second, and add helper functions to control
Runtime PM during probe and remove.

Read quirks from i3c_hci_driver_ids[] and set new quirk
HCI_QUIRK_RPM_ALLOWED for intel-lpss-i3c devices to enable runtime PM for
them.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20260113072702.16268-21-adrian.hunter@intel.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

authored by

Adrian Hunter and committed by
Alexandre Belloni
b9a15012 990c149c

+72 -6
+70 -6
drivers/i3c/master/mipi-i3c-hci/core.c
··· 16 16 #include <linux/module.h> 17 17 #include <linux/platform_data/mipi-i3c-hci.h> 18 18 #include <linux/platform_device.h> 19 + #include <linux/pm_runtime.h> 19 20 20 21 #include "hci.h" 21 22 #include "ext_caps.h" ··· 183 182 int irq = platform_get_irq(pdev, 0); 184 183 185 184 reg_write(INTR_SIGNAL_ENABLE, 0x0); 185 + hci->irq_inactive = true; 186 186 synchronize_irq(irq); 187 187 } 188 188 ··· 566 564 irqreturn_t result = IRQ_NONE; 567 565 u32 val; 568 566 567 + /* 568 + * The IRQ can be shared, so the handler may be called when the IRQ is 569 + * due to a different device. That could happen when runtime suspended, 570 + * so exit immediately if IRQs are not expected for this device. 571 + */ 572 + if (hci->irq_inactive) 573 + return IRQ_NONE; 574 + 569 575 val = reg_read(INTR_STATUS); 570 576 reg_write(INTR_STATUS, val); 571 577 dev_dbg(&hci->master.dev, "INTR_STATUS %#x", val); ··· 733 723 return 0; 734 724 } 735 725 726 + static int i3c_hci_runtime_suspend(struct device *dev) 727 + { 728 + struct i3c_hci *hci = dev_get_drvdata(dev); 729 + int ret; 730 + 731 + ret = i3c_hci_bus_disable(hci); 732 + if (ret) 733 + return ret; 734 + 735 + hci->io->suspend(hci); 736 + 737 + return 0; 738 + } 739 + 740 + static int i3c_hci_runtime_resume(struct device *dev) 741 + { 742 + struct i3c_hci *hci = dev_get_drvdata(dev); 743 + int ret; 744 + 745 + ret = i3c_hci_reset_and_init(hci); 746 + if (ret) 747 + return -EIO; 748 + 749 + i3c_hci_set_master_dyn_addr(hci); 750 + 751 + mipi_i3c_hci_dat_v1.restore(hci); 752 + 753 + hci->irq_inactive = false; 754 + 755 + hci->io->resume(hci); 756 + 757 + reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE); 758 + 759 + return 0; 760 + } 761 + 762 + #define DEFAULT_AUTOSUSPEND_DELAY_MS 1000 763 + 764 + static void i3c_hci_rpm_enable(struct device *dev) 765 + { 766 + struct i3c_hci *hci = dev_get_drvdata(dev); 767 + 768 + pm_runtime_set_autosuspend_delay(dev, DEFAULT_AUTOSUSPEND_DELAY_MS); 769 + pm_runtime_use_autosuspend(dev); 770 + devm_pm_runtime_set_active_enabled(dev); 771 + 772 + hci->master.rpm_allowed = true; 773 + } 774 + 736 775 static int i3c_hci_init(struct i3c_hci *hci) 737 776 { 738 777 bool size_in_dwords; ··· 900 841 hci->master.dev.init_name = dev_name(&pdev->dev); 901 842 902 843 hci->quirks = (unsigned long)device_get_match_data(&pdev->dev); 844 + if (!hci->quirks && platform_get_device_id(pdev)) 845 + hci->quirks = platform_get_device_id(pdev)->driver_data; 903 846 904 847 ret = i3c_hci_init(hci); 905 848 if (ret) ··· 913 852 if (ret) 914 853 return ret; 915 854 916 - ret = i3c_master_register(&hci->master, &pdev->dev, 917 - &i3c_hci_ops, false); 918 - if (ret) 919 - return ret; 855 + if (hci->quirks & HCI_QUIRK_RPM_ALLOWED) 856 + i3c_hci_rpm_enable(&pdev->dev); 920 857 921 - return 0; 858 + return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false); 922 859 } 923 860 924 861 static void i3c_hci_remove(struct platform_device *pdev) ··· 939 880 MODULE_DEVICE_TABLE(acpi, i3c_hci_acpi_match); 940 881 941 882 static const struct platform_device_id i3c_hci_driver_ids[] = { 942 - { .name = "intel-lpss-i3c" }, 883 + { .name = "intel-lpss-i3c", HCI_QUIRK_RPM_ALLOWED }, 943 884 { /* sentinel */ } 944 885 }; 945 886 MODULE_DEVICE_TABLE(platform, i3c_hci_driver_ids); 887 + 888 + static const struct dev_pm_ops i3c_hci_pm_ops = { 889 + RUNTIME_PM_OPS(i3c_hci_runtime_suspend, i3c_hci_runtime_resume, NULL) 890 + }; 946 891 947 892 static struct platform_driver i3c_hci_driver = { 948 893 .probe = i3c_hci_probe, ··· 956 893 .name = "mipi-i3c-hci", 957 894 .of_match_table = of_match_ptr(i3c_hci_of_match), 958 895 .acpi_match_table = i3c_hci_acpi_match, 896 + .pm = pm_ptr(&i3c_hci_pm_ops), 959 897 }, 960 898 }; 961 899 module_platform_driver(i3c_hci_driver);
+2
drivers/i3c/master/mipi-i3c-hci/hci.h
··· 51 51 void *io_data; 52 52 const struct hci_cmd_ops *cmd; 53 53 atomic_t next_cmd_tid; 54 + bool irq_inactive; 54 55 u32 caps; 55 56 unsigned int quirks; 56 57 unsigned int DAT_entries; ··· 145 144 #define HCI_QUIRK_PIO_MODE BIT(2) /* Set PIO mode for AMD platforms */ 146 145 #define HCI_QUIRK_OD_PP_TIMING BIT(3) /* Set OD and PP timings for AMD platforms */ 147 146 #define HCI_QUIRK_RESP_BUF_THLD BIT(4) /* Set resp buf thld to 0 for AMD platforms */ 147 + #define HCI_QUIRK_RPM_ALLOWED BIT(5) /* Runtime PM allowed */ 148 148 149 149 /* global functions */ 150 150 void mipi_i3c_hci_resume(struct i3c_hci *hci);