summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/host1x/mipi.c
diff options
context:
space:
mode:
authorFuad Tabba <fuad.tabba@linux.dev>2026-09-22 19:14:30 +0100
committerWill Deacon <will@kernel.org>2026-09-23 12:34:30 +0000
commit2bc6b218717b9d08f466f88209251d54bc09b207 (patch)
treecb91c941eccb15f94cbec4e96aa3a9c8736b5d66 /drivers/gpu/host1x/mipi.c
downloadlinux-stable-2bc6b218717b9d08f466f88209251d54bc09b207.tar.gz
linux-stable-2bc6b218717b9d08f466f88209251d54bc09b207.zip
arm64/boot: Disable trapping of PMZR_EL0 writes to EL2grafted
__init_el2_fgt2() writes one mask to both HDFGRTR2_EL2 and HDFGWTR2_EL2. PMZR_EL0 is write-only, so its trap bit, nPMZR_EL0, exists only in HDFGWTR2_EL2 and is therefore never set: a PMZR_EL0 write from the host traps to EL2, where the nVHE hypervisor has no handler and BUG()s. The kernel never writes PMZR_EL0, but kernel.perf_user_access=1 has the PMU driver set PMUSERENR_EL0.UEN for a task with a user-read event, so a write from EL0 reaches the trap and takes the host down without a panic message. Accumulate the HDFGWTR2_EL2 bits separately, as __init_el2_fgt() already does for HDFGWTR_EL2, and set nPMZR_EL0 with the other FEAT_PMUv3p9 bits. Fixes: 858c7bfcb35e1 ("arm64/boot: Enable EL2 requirements for FEAT_PMUv3p9") Cc: stable@vger.kernel.org Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev> Reviewed-by: Anshuman Khandual <anshuman.khandual@arm.com> Reviewed-by: Oliver Upton <oupton@kernel.org> Signed-off-by: Will Deacon <will@kernel.org>
Diffstat (limited to 'drivers/gpu/host1x/mipi.c')
-rw-r--r--drivers/gpu/host1x/mipi.c195
1 files changed, 195 insertions, 0 deletions
diff --git a/drivers/gpu/host1x/mipi.c b/drivers/gpu/host1x/mipi.c
new file mode 100644
index 000000000..988681423
--- /dev/null
+++ b/drivers/gpu/host1x/mipi.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ * Copyright (C) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/tegra-mipi-cal.h>
+
+/* only need to support one provider */
+static struct {
+ struct device_node *np;
+ const struct tegra_mipi_ops *ops;
+} provider;
+
+/**
+ * tegra_mipi_enable - Enable the Tegra MIPI calibration device.
+ * @device: Handle to the Tegra MIPI calibration device.
+ *
+ * This calls the enable sequence for the Tegra MIPI calibration device.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int tegra_mipi_enable(struct tegra_mipi_device *device)
+{
+ if (device->ops->enable)
+ return device->ops->enable(device);
+
+ return 0;
+}
+EXPORT_SYMBOL(tegra_mipi_enable);
+
+/**
+ * tegra_mipi_disable - Disable the Tegra MIPI calibration device.
+ * @device: Handle to the Tegra MIPI calibration device.
+ *
+ * This calls the disable sequence for the Tegra MIPI calibration device.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int tegra_mipi_disable(struct tegra_mipi_device *device)
+{
+ if (device->ops->disable)
+ return device->ops->disable(device);
+
+ return 0;
+}
+EXPORT_SYMBOL(tegra_mipi_disable);
+
+/**
+ * tegra_mipi_start_calibration - Start the Tegra MIPI calibration sequence.
+ * @device: Handle to the Tegra MIPI calibration device.
+ *
+ * This initiates the calibration of CSI/DSI interfaces via the Tegra MIPI
+ * calibration device.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int tegra_mipi_start_calibration(struct tegra_mipi_device *device)
+{
+ if (device->ops->start_calibration)
+ return device->ops->start_calibration(device);
+
+ return 0;
+}
+EXPORT_SYMBOL(tegra_mipi_start_calibration);
+
+/**
+ * tegra_mipi_finish_calibration - Finish the Tegra MIPI calibration sequence.
+ * @device: Handle to the Tegra MIPI calibration device.
+ *
+ * This completes the calibration of CSI/DSI interfaces via the Tegra MIPI
+ * calibration device.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int tegra_mipi_finish_calibration(struct tegra_mipi_device *device)
+{
+ if (device->ops->finish_calibration)
+ return device->ops->finish_calibration(device);
+
+ return 0;
+}
+EXPORT_SYMBOL(tegra_mipi_finish_calibration);
+
+/**
+ * tegra_mipi_request - Request a Tegra MIPI calibration device.
+ * @device: Handle of the device requesting the MIPI calibration function.
+ * @np: Device node pointer of the device requesting the MIPI calibration
+ * function.
+ *
+ * This function requests a reference to a Tegra MIPI calibration device.
+ *
+ * Returns a pointer to the Tegra MIPI calibration device on success,
+ * or an ERR_PTR-encoded error code on failure.
+ */
+struct tegra_mipi_device *tegra_mipi_request(struct device *device,
+ struct device_node *np)
+{
+ struct tegra_mipi_device *mipidev;
+ struct of_phandle_args args;
+ int err;
+
+ err = of_parse_phandle_with_args(np, "nvidia,mipi-calibrate",
+ "#nvidia,mipi-calibrate-cells", 0,
+ &args);
+ if (err < 0)
+ return ERR_PTR(err);
+
+ if (provider.np != args.np) {
+ err = -ENODEV;
+ goto out;
+ }
+
+ mipidev = kzalloc_obj(*mipidev);
+ if (!mipidev) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ mipidev->pdev = of_find_device_by_node(args.np);
+ if (!mipidev->pdev) {
+ err = -ENODEV;
+ goto free;
+ }
+
+ of_node_put(args.np);
+
+ mipidev->ops = provider.ops;
+ mipidev->pads = args.args[0];
+
+ return mipidev;
+
+free:
+ kfree(mipidev);
+out:
+ of_node_put(args.np);
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL(tegra_mipi_request);
+
+/**
+ * tegra_mipi_free - Free a Tegra MIPI calibration device.
+ * @mipidev: Handle to the Tegra MIPI calibration device.
+ *
+ * This function releases a reference to a Tegra MIPI calibration device
+ * previously requested by tegra_mipi_request().
+ */
+void tegra_mipi_free(struct tegra_mipi_device *mipidev)
+{
+ platform_device_put(mipidev->pdev);
+ kfree(mipidev);
+}
+EXPORT_SYMBOL(tegra_mipi_free);
+
+static void tegra_mipi_remove_provider(void *data)
+{
+ provider.np = NULL;
+ provider.ops = NULL;
+}
+
+/**
+ * devm_tegra_mipi_add_provider - Managed registration of a Tegra MIPI
+ * calibration function provider.
+ * @device: Handle to the device providing the MIPI calibration function.
+ * @np: Device node pointer of the device providing the MIPI calibration
+ * function.
+ * @ops: Operations supported by the MIPI calibration device.
+ *
+ * This registers a device that provides MIPI calibration functions.
+ * For Tegra20 and Tegra30, this is the CSI block, while Tegra114 and
+ * newer SoC generations have a dedicated hardware block for these
+ * functions.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int devm_tegra_mipi_add_provider(struct device *device, struct device_node *np,
+ const struct tegra_mipi_ops *ops)
+{
+ if (provider.np)
+ return -EBUSY;
+
+ provider.np = np;
+ provider.ops = ops;
+
+ return devm_add_action_or_reset(device, tegra_mipi_remove_provider, NULL);
+}
+EXPORT_SYMBOL(devm_tegra_mipi_add_provider);