#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/debugfs.h>
+#include <linux/i3c/master.h>
#include <linux/idr.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/pm_qos.h>
#include <linux/pm_runtime.h>
+#include "hci.h"
+
/*
* There can up to 15 instances, but implementations have at most 2 at this
* time.
*/
#define INST_MAX 2
+struct mipi_i3c_hci_pci_instance {
+ struct device *dev;
+ bool operational;
+};
+
struct mipi_i3c_hci_pci {
struct pci_dev *pci;
void __iomem *base;
const struct mipi_i3c_hci_pci_info *info;
+ struct mipi_i3c_hci_pci_instance instance[INST_MAX];
void *private;
};
int id[INST_MAX];
u32 instance_offset[INST_MAX];
int instance_count;
+ bool control_instance_pm;
};
#define INTEL_PRIV_OFFSET 0x2b0
.instance_count = 1,
};
+static int mipi_i3c_hci_pci_find_instance(struct mipi_i3c_hci_pci *hci, struct device *dev)
+{
+ for (int i = 0; i < INST_MAX; i++) {
+ if (!hci->instance[i].dev)
+ hci->instance[i].dev = dev;
+ if (hci->instance[i].dev == dev)
+ return i;
+ }
+
+ return -1;
+}
+
+#define HC_CONTROL 0x04
+#define HC_CONTROL_BUS_ENABLE BIT(31)
+
+static bool __mipi_i3c_hci_pci_is_operational(struct device *dev)
+{
+ const struct mipi_i3c_hci_platform_data *pdata = dev->platform_data;
+ u32 hc_control = readl(pdata->base_regs + HC_CONTROL);
+
+ return hc_control & HC_CONTROL_BUS_ENABLE;
+}
+
+static bool mipi_i3c_hci_pci_is_operational(struct device *dev, bool update)
+{
+ struct mipi_i3c_hci_pci *hci = dev_get_drvdata(dev->parent);
+ int pos = mipi_i3c_hci_pci_find_instance(hci, dev);
+
+ if (pos < 0) {
+ dev_err(dev, "%s: I3C instance not found\n", __func__);
+ return false;
+ }
+
+ if (update)
+ hci->instance[pos].operational = __mipi_i3c_hci_pci_is_operational(dev);
+
+ return hci->instance[pos].operational;
+}
+
+struct mipi_i3c_hci_pci_pm_data {
+ struct device *dev[INST_MAX];
+ int dev_cnt;
+};
+
+static bool mipi_i3c_hci_pci_is_mfd(struct device *dev)
+{
+ return dev_is_platform(dev) && mfd_get_cell(to_platform_device(dev));
+}
+
+static int mipi_i3c_hci_pci_suspend_instance(struct device *dev, void *data)
+{
+ struct mipi_i3c_hci_pci_pm_data *pm_data = data;
+ int ret;
+
+ if (!mipi_i3c_hci_pci_is_mfd(dev) ||
+ !mipi_i3c_hci_pci_is_operational(dev, true))
+ return 0;
+
+ ret = i3c_hci_rpm_suspend(dev);
+ if (ret)
+ return ret;
+
+ pm_data->dev[pm_data->dev_cnt++] = dev;
+
+ return 0;
+}
+
+static int mipi_i3c_hci_pci_resume_instance(struct device *dev, void *data)
+{
+ struct mipi_i3c_hci_pci_pm_data *pm_data = data;
+ int ret;
+
+ if (!mipi_i3c_hci_pci_is_mfd(dev) ||
+ !mipi_i3c_hci_pci_is_operational(dev, false))
+ return 0;
+
+ ret = i3c_hci_rpm_resume(dev);
+ if (ret)
+ return ret;
+
+ pm_data->dev[pm_data->dev_cnt++] = dev;
+
+ return 0;
+}
+
+static int mipi_i3c_hci_pci_suspend(struct device *dev)
+{
+ struct mipi_i3c_hci_pci *hci = dev_get_drvdata(dev);
+ struct mipi_i3c_hci_pci_pm_data pm_data = {};
+ int ret;
+
+ if (!hci->info->control_instance_pm)
+ return 0;
+
+ ret = device_for_each_child_reverse(dev, &pm_data, mipi_i3c_hci_pci_suspend_instance);
+ if (ret)
+ for (int i = 0; i < pm_data.dev_cnt; i++)
+ i3c_hci_rpm_resume(pm_data.dev[i]);
+
+ return ret;
+}
+
+static int mipi_i3c_hci_pci_resume(struct device *dev)
+{
+ struct mipi_i3c_hci_pci *hci = dev_get_drvdata(dev);
+ struct mipi_i3c_hci_pci_pm_data pm_data = {};
+ int ret;
+
+ if (!hci->info->control_instance_pm)
+ return 0;
+
+ ret = device_for_each_child(dev, &pm_data, mipi_i3c_hci_pci_resume_instance);
+ if (ret)
+ for (int i = 0; i < pm_data.dev_cnt; i++)
+ i3c_hci_rpm_suspend(pm_data.dev[i]);
+
+ return ret;
+}
+
static void mipi_i3c_hci_pci_rpm_allow(struct device *dev)
{
pm_runtime_put(dev);
/* PM ops must exist for PCI to put a device to a low power state */
static const struct dev_pm_ops mipi_i3c_hci_pci_pm_ops = {
+ RUNTIME_PM_OPS(mipi_i3c_hci_pci_suspend, mipi_i3c_hci_pci_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(mipi_i3c_hci_pci_suspend, mipi_i3c_hci_pci_resume)
};
static const struct pci_device_id mipi_i3c_hci_pci_devices[] = {